简体   繁体   English

JavaScript:回调函数是如何工作的?

[英]JavaScript: How does a callback function work?

I'm really new to JS and I'm having a lot of trouble writing/understanding callback functions Let's say for example, I have the following code, but i dont want我对 JS 真的很陌生,我在编写/理解回调函数时遇到了很多麻烦让我们说,例如,我有以下代码,但我不想要

takeNumbersGreaterThan(5);

to be executed until执行到

 insertNumbers();

is finished完成了

numbers = [];   
 greaterThan = []; 
insertNumbers();
takeNumbersGreaterThan(5);

insertNumbers(){
  for (var i = 0; i<11; i++)
  {
    numbers.push(i)
  }
}

takeNumbersGreaterThan(number){

 for (var m = 0; m<numbers.length; m++)
  {
    if (numbers[m] > number)
      {
         greaterThan.push(numbers[m])
      }
  }
 }

How do I go about doing that?我该怎么做?

The basics (not about callbacks, but about programming languages)基础知识(不是关于回调,而是关于编程语言)

To understand callbacks first you have to understand functions.要了解回调,您必须先了解函数。 And to understand functions in javascript you first have to understand variables, values and functions.要理解 JavaScript 中的函数,您首先必须理解变量、值和函数。

Almost all programming language can deal with values.几乎所有的编程语言都可以处理值。 So if you've done any programming you'd have a basic idea of what values are (I'm going to greatly simplify types of values here and refer to both values and references/pointers as "values").所以如果你做过任何编程,你就会对什么是值有一个基本的了解(我将在这里大大简化值的类型,并将值和引用/指针都称为“值”)。

A value is a thing.价值是一种东西。 For example a number or a string.例如一个数字或一个字符串。 So 22.31 is a value and "Hello Dave" is a value.所以22.31是一个值, "Hello Dave"是一个值。

Most languages also have the concept of variables (not all do though).大多数语言也有变量的概念(但并非所有语言都有)。 A variable is a "name" we give to values to make it easier to process values.变量是我们赋予值的“名称”,以便更轻松地处理值。 For example, in the following, x is a variable:例如,在下面, x是一个变量:

var x = 12;

.. and it's value is 12. .. 它的值为 12。

What do variables allow us to do?变量允许我们做什么? It allows us to substitute a value for a name in our calculations.它允许我们在计算中用一个值代替一个名称。 Just like math.就像数学一样。 For example, if x is 12 and we know we can add 1 to 12 we can also do:例如,如果x12并且我们知道我们可以将1添加到12我们也可以这样做:

x + 1

Functions are values函数就是值

In javascript functions are values.在 javascript 中,函数是值。 For example, in the following we assign a function to a variable:例如,在下面我们将一个函数分配给一个变量:

function a () {return "Hello"}
var y = a;

Since what variables do is to allow you to substitute a name for a value then if you can call the function a using the syntax a() it means you can also do this with the variable y :由于变量的作用是允许您用名称替换值,因此如果您可以使用语法a()调用函数a ,则意味着您也可以使用变量y执行此操作:

y(); // returns "Hello"

Callbacks回调

If functions are values it also means that you can pass functions as arguments to other functions.如果函数是值,这也意味着您可以将函数作为参数传递给其他函数。 For example, the following is how you'd normally call a function in another function:例如,以下是您通常在另一个函数中调用函数的方式:

function a () {return "Hello"}

function b () {return a() + " World"}

b(); // returns "Hello World"

If you can pass functions as a variable, it means you can do something like this:如果您可以将函数作为变量传递,则意味着您可以执行以下操作:

function a () {return "Hello"}
function b () {return "Bye"}

function c (functionVariable) {return functionVariable() + " World"}

c(a); // returns "Hello World"
c(b); // returns "Bye World"

As you can see.如你看到的。 Callbacks are not special at all.回调根本没有什么特别之处。 They're just the result of the fact that in javascript functions obey the same rules as other values like numbers, arrays and strings.它们只是在 javascript 函数中遵循与其他值(如数字、数组和字符串)相同的规则这一事实的结果。

Callbacks does not mean asynchronous回调并不意味着异步

As you can see from the example above, both calls to the function c return a value.从上面的示例中可以看出,对函数c两次调用都返回一个值。 Thus the function c is not asynchronous even though it accepts a callback.因此,即使函数c接受回调,它也不是异步的。 So callbacks can be used for both synchronous and asynchronous code.因此回调可用于同步和异步代码。

A good example of a synchronous callback is the Array.sort() method.同步回调的一个很好的例子是Array.sort()方法。 It sorts an array but accepts an optional callback for you to define how to sort (alphabetically, numerically, by last name etc.).它对数组进行排序,但接受一个可选的回调来定义如何排序(按字母顺序、数字顺序、姓氏等)。

Why asynchronous code need callbacks为什么异步代码需要回调

For now forget about ajax or networking code.现在忘记 ajax 或网络代码。 Let's instead look at a scenario that makes it even more obvious why callbacks are used by asynchronous code.让我们来看看一个场景,让异步代码使用回调的原因更加明显。

Say for example you have a button.比如说你有一个按钮。 Now, when the user click this button you want something to happen.现在,当用户单击此按钮时,您希望发生某些事情。 How do you do that?你是怎样做的?

The first thing most people do is probably something like this:大多数人做的第一件事可能是这样的:

while (1) {
    if (button.isClicked) {
        doSomething();
    }
}

OK.好的。 So that's an infinite loop that only checks if the button is clicked and nothing else.所以这是一个无限循环,它只检查按钮是否被点击,而不检查其他任何东西。 Then how do you expect the browser to update the UI and track the mouse?那么你期望浏览器如何更新 UI 和跟踪鼠标呢? This leads us to the next thing people try to do:这将我们引向人们尝试做的下一件事:

while (1) {
    if (button.isClicked) {
        doSomething();
    }
    else {
        updateUI();
    }
}

OK.好的。 Great.伟大的。 But two problems.但是有两个问题。 First, if someone were to write a library like Google Charts or jQuery or anything to do with the UI they either will write their own while(1)... loop or you must manually copy/paste their function into your while loop.首先,如果有人要编写像 Google Charts 或 jQuery 之类的库或任何与 UI 相关的东西,他们要么编写自己的while(1)...循环,要么您必须手动将它们的函数复制/粘贴到 while 循环中。 This does not scale.这没有规模。 Second and more importantly, this is inefficient.其次,更重要的是,这是低效的。 That while loop will use 100% CPU time checking a button.该 while 循环将使用 100% CPU 时间检查按钮。 Wouldn't it be nicer if the browser can tell us when the button is clicked.如果按钮被点击时浏览器可以告诉我们,那不是更好吗?

Fortunately in javascript functions are just values.幸运的是,在 javascript 中,函数只是值。 You can pass a function to the browser and tell it to execute your function when someone clicks a button:您可以将一个函数传递给浏览器,并告诉它在有人单击按钮时执行您的函数:

button.addEventListener('click', doSomething);

Note: Notice the difference between treating a function as a variable and calling a function.注意:请注意将函数视为变量和调用函数之间的区别。 If you want to treat a function as a variable just use the name.如果要将函数视为变量,只需使用名称即可。 If you want to call a function use braces like doSomething() .如果你想调用一个函数,请使用像doSomething()这样的大括号。

Why everyone insist on writing asynchronous functions为什么大家都坚持写异步函数

There are two reasons why everyone seem to insist on making asynchronous APIs, especially in languages like javascript.每个人似乎都坚持制作异步 API 有两个原因,尤其是在像 javascript 这样的语言中。

First, the low-level file and network I/O are async.首先,低级文件和网络 I/O 是异步的。 This means that if you want to talk to a database or a server or read a file you need to implement it as asynchronous.这意味着如果您想与数据库或服务器对话或读取文件,您需要将其实现为异步。 Also, javascript is single threaded.此外,javascript 是单线程的。 So if you use the synchronous versions of I/O functions you will freeze everything else.因此,如果您使用 I/O 函数的同步版本,您将冻结其他所有内容。

Second, it turns out that in a lot of cases (but certainly not in all) asynchronous, single-threaded programming is as fast as or sometimes even faster than synchronous multi-threaded programming.其次,事实证明,在很多情况下(但肯定不是全部)异步单线程编程与同步多线程编程一样快,有时甚至更快。

Combined, the two reasons above creates social pressure in the js community to ensure that all I/O code are asynchronous in order to maintain the speed advantage and not block other people's code.综合以上两个原因,在js社区造成了社会压力,需要确保所有的I/O代码都是异步的,以保持速度优势,而不是阻塞别人的代码。

If I understand correctly, you want to know more about callbacks and you want to use them.如果我理解正确,您想了解更多有关回调的信息并想使用它们。 Let me try too help you using your code.让我也尝试帮助您使用您的代码。

If you want to execute takeNumbersGreaterThan(5);如果要执行takeNumbersGreaterThan(5); right after insertNumbers();insertNumbers(); using callback function, you could do something like this:使用回调函数,你可以做这样的事情:

numbers = [];   
greaterThan = []; 
insertNumbers(takeNumbersGreaterThan, 5);

function insertNumbers(callbackFunction, callbackFunctionParam){
  for (var i = 0; i<11; i++)
  {
    numbers.push(i)
  }
  callbackFunction(callbackFunctionParam);
}

function takeNumbersGreaterThan(number){

 for (var m = 0; m<numbers.length; m++)
  {
    if (numbers[m] > number)
      {
         greaterThan.push(numbers[m])
      }
  }
 }

but this is just a simple example of how you can call a callback function after some computation.但这只是一个简单的例子,说明如何在一些计算后调用回调函数。 This code could be improved.这段代码可以改进。 The point is, you can pass your callback function as parameter on your function and then later execute this callback function.关键是,您可以将回调函数作为参数传递给函数,然后再执行此回调函数。

You are already there.你已经在那里了。 Your code is almost completely correct.您的代码几乎完全正确。 You was just missing function keywork declaration.您只是缺少功能键声明。

The script below, shows you how to run takeNumbersGreaterThan after insertNumbers.下面的脚本向您展示了如何在 insertNumbers 之后运行 takeNumbersGreaterThan。 In my sample I also changed the function sign in order to pass array as parameters and avoid some of one common "mistakes" known as closures.在我的示例中,我还更改了函数符号,以便将数组作为参数传递并避免一些称为闭包的常见“错误”。

 var numbers = []; var greaterThan = []; var insertNumbers = function(numbers) { for (var i = 0; i<11; i++) numbers.push(i) } var takeNumbersGreaterThan = function(number, numbers, greaterThan){ for (var m = 0; m<numbers.length; m++) { if (numbers[m] > number) greaterThan.push(numbers[m]); } } // run insert numbers insertNumbers(numbers); // run take numbers greater than takeNumbersGreaterThan(5, numbers, greaterThan); // log document.write(greaterThan);

Your code dosen't use any asyncronous calls so you wouldent need to use any callbacks to handle the execution.您的代码不使用任何异步调用,因此您不需要使用任何回调来处理执行。 But if you would like to know how to do it, this would be the way.但如果你想知道怎么做,这就是方法。

numbers = [];   
greaterThan = []; 

function insertNumbers(callback){
  for (var i = 0; i<11; i++)
  {
    numbers.push(i)
  }

  callback(); // now execute the callback funtion
}

function takeNumbersGreaterThan(number){

 for (var m = 0; m<numbers.length; m++)
  {
    if (numbers[m] > number)
      {
         greaterThan.push(numbers[m]);
      }
  }
  console.log(greaterThan);
 }

 insertNumbers(function() { // here we send a functions as argument to insertNumbers which will execute when callback() is called
  takeNumbersGreaterThan(5);
});

insertNumbers takes a argument called "callback". insertNumbers接受一个名为“回调”的参数。 When insertNumbers is finished we simply run callback() .insertNumbers完成后,我们只需运行callback() In the initial call to insertNumber we pass a function as argument which will be executed as soon as insertNumers finished (or callback() is called).在对insertNumber的初始调用中,我们传递了一个函数作为参数,该函数将在insertNumers完成(或callback()被调用)后立即执行。

Code (for the most part) is executed sequentially.代码(大部分)是按顺序执行的。 In the code you've provided, the computer runs though the code in the order you've provided it.在您提供的代码中,计算机按照您提供的顺序运行代码。 First it creates a new array object and sets it to the numbers variable, then it creates a new array object and sets it to the greaterThan variable.首先它创建一个新的数组对象并将其设置为 numbers 变量,然后它创建一个新的数组对象并将其设置为 GreaterThan 变量。 Then, it runs the insertNumbers function.然后,它运行 insertNumbers 函数。 Now what the computer does is jump to the code that you've defined in insertNumbers and executes all that code.现在计算机所做的是跳转到您在 insertNumbers 中定义的代码并执行所有这些代码。 Then, after it finishes with that, it'll return to executing the initial thread of code it was on which is back at line 4. So now it'll jump to the takeNumbersGreaterThan code.然后,在它完成之后,它将返回执行它所在的初始代码线程,该线程又回到第 4 行。所以现在它将跳转到 takeNumbersGreaterThan 代码。 So functionally, you don't need any callbacks since your code doesn't do anything that takes an arbitrary amount of time.因此,在功能上,您不需要任何回调,因为您的代码不会执行任何需要任意时间的事情。

That being explained, you see that takeNumbersGreaterThan doesn't get executed until after insertNumbers is executed.对此进行了解释,您会看到 takeNumbersGreaterThan 直到执行 insertNumbers 后才会执行。

The only time code isn't executed sequentially is when you start doing multi core/threaded code.唯一不按顺序执行代码的时间是您开始执行多核/线程代码时。

Callbacks are used when something takes an arbitrary amount of time like when you are reading data from a disk or are requesting data from the network.当某些事情需要任意时间时使用回调,例如当您从磁盘读取数据或从网络请求数据时。

Callbacks can exist, because functions defined in javascript (and many other langauges) exist as objects in the code.回调可以存在,因为在 javascript(和许多其他语言)中定义的函数在代码中作为对象存在。 If you don't put the parentheses after a function name, you're actually referencing the function object just like any other variable.如果您没有在函数名后放置括号,您实际上是在引用函数对象,就像任何其他变量一样。 So you can pass that function object around in your code and to other bits of code.所以你可以在你的代码和其他代码中传递这个函数对象。 That is what is happening in this example.这就是这个例子中发生的事情。

setTimeout(myCallback, 5000)

function myCallback(){
    console.log("5 seconds have passed");
}

So, as you can see, I can take my function myCallback and give it to another function, in this instance setTimeout , to use after the other function has completed a task.因此,如您所见,我可以将我的函数myCallback交给另一个函数,在本例中为setTimeout ,以便在另一个函数完成任务后使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM