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

JavaScript: How does a callback function work?

提问人:Darren 提问时间:3/15/2017 更新时间:3/21/2019 访问量:769

问:

我真的是 JS 的新手,我在编写/理解回调函数时遇到了很多麻烦 例如,假设我有以下代码,但我不想

takeNumbersGreaterThan(5);

执行至

 insertNumbers();

已完成

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])
      }
  }
 }

我该怎么做?

JavaScript的

评论

0赞 Pointy 3/15/2017
您的代码已经以这种方式工作。没有回调。
0赞 Valter Júnior 3/15/2017
您实际上没有使用任何回调!您确定要了解回调吗?
0赞 Darren 3/15/2017
我试图制作的程序要复杂得多,我这样调用函数,但是第二个函数总是失败,因为第一个函数还没有完成(第一个函数需要很长时间)
0赞 Artyom Neustroev 3/15/2017
您无需在代码中执行任何异步操作 - 回调用于处理这些操作的流。
0赞 Barmar 3/15/2017
你的基本语法是错误的,你在函数定义之前缺少关键字。function

答:

0赞 Valter Júnior 3/15/2017 #1

如果我理解正确,您想了解更多关于回调的信息,并且想要使用它们。让我也尝试帮助您使用您的代码。

如果你想在使用回调函数后立即执行,你可以做这样的事情:takeNumbersGreaterThan(5);insertNumbers();

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])
      }
  }
 }

但这只是一个简单的示例,说明如何在一些计算后调用回调函数。此代码可以改进。关键是,您可以将回调函数作为函数上的参数传递,然后稍后执行此回调函数。

0赞 gio 3/15/2017 #2

你已经在那里了。您的代码几乎完全正确。 您只是缺少函数键声明。

下面的脚本显示了如何在 insertNumbers 之后运行 takeNumbersGreaterThan。在我的示例中,我还更改了函数符号,以便将数组作为参数传递,并避免一些常见的“错误”,称为闭包。

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);

0赞 Rikard Askelöf 3/15/2017 #3

您的代码不使用任何异步调用,因此您需要使用任何回调来处理执行。但是,如果您想知道如何去做,这就是方法。

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采用一个名为“callback”的参数。完成后,我们只需运行.在对 的初始调用中,我们传递一个函数作为参数,该函数将在完成(或被调用)后立即执行。insertNumberscallback()insertNumberinsertNumerscallback()

0赞 BAM5 3/15/2017 #4

代码(大部分)是按顺序执行的。在你提供的代码中,计算机将按照你提供的顺序运行代码。首先,它创建一个新的数组对象并将其设置为 numbers 变量,然后创建一个新的数组对象并将其设置为 greaterThan 变量。 然后,它运行 insertNumbers 函数。现在,计算机所做的是跳转到您在 insertNumbers 中定义的代码并执行所有这些代码。然后,在完成此操作后,它将返回执行它所在的初始代码线程,该线程位于第 4 行。因此,现在它将跳转到 takeNumbersGreaterThan 代码。因此,从功能上讲,您不需要任何回调,因为您的代码不会执行任何需要任意时间的事情。

在解释之后,您会看到 takeNumbersGreaterThan 直到执行 insertNumbers 之后才会被执行。

唯一不按顺序执行代码的时间是当您开始执行多核/线程代码时。

当某些东西需要任意时间时,例如从磁盘读取数据或从网络请求数据时,会使用回调。

回调可以存在,因为用 javascript(和许多其他语言)定义的函数在代码中作为对象存在。如果不在函数名称后面加上括号,则实际上引用函数对象就像引用任何其他变量一样。因此,您可以在代码中将该函数对象传递给其他代码位。这就是此示例中发生的情况。

setTimeout(myCallback, 5000)

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

所以,正如你所看到的,我可以把我的函数交给另一个函数,在这种情况下,在另一个函数完成任务后使用。myCallbacksetTimeout

4赞 slebetman 3/16/2017 #5

基础知识(不是关于回调,而是关于编程语言)

要理解回调,首先必须了解函数。要理解 javascript 中的函数,您首先必须了解变量、值和函数。

几乎所有的编程语言都可以处理值。因此,如果您做过任何编程,您就会对值有一个基本的概念(我将在这里大大简化值的类型,并将值和引用/指针都称为“值”)。

价值就是事物。例如,数字或字符串。所以是一个值,也是一个值。22.31"Hello Dave"

大多数语言也有变量的概念(但并非所有语言都有)。变量是我们给值起的“名称”,以便于处理值。例如,在下面的 是一个变量:x

var x = 12;

..它的值是 12。

变量允许我们做什么?它允许我们在计算中用值代替名称。就像数学一样。例如,如果 is 并且我们知道我们可以添加,我们也可以执行以下操作:x12112

x + 1

函数是值

在 javascript 中,函数是值。例如,在下文中,我们将一个函数分配给一个变量:

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

由于变量的作用是允许您用名称代替值,那么如果您可以使用语法调用该函数,则意味着您也可以使用变量执行此操作:aa()y

y(); // returns "Hello"

回调

如果函数是值,这也意味着您可以将函数作为参数传递给其他函数。例如,以下是您通常在另一个函数中调用函数的方式:

function a () {return "Hello"}

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

b(); // returns "Hello World"

如果可以将函数作为变量传递,则意味着可以执行以下操作:

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

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

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

如您所见。回调一点也不特别。它们只是因为在 javascript 中,函数遵循与其他值(如数字、数组和字符串)相同的规则。

回调并不意味着异步

从上面的示例中可以看出,对函数的两次调用都返回一个值。因此,即使该函数接受回调,它也不是异步的。因此,回调既可用于同步代码,也可用于异步代码。cc

同步回调的一个很好的例子是方法。它对数组进行排序,但接受一个可选的回调,以便您定义如何排序(按字母、数字、按姓氏等)。Array.sort()

为什么异步代码需要回调

现在,忘记ajax或网络代码。让我们看一个场景,它使异步代码使用回调的原因更加明显。

例如,假设你有一个按钮。现在,当用户单击此按钮时,您希望发生某些事情。你是怎么做到的?

大多数人做的第一件事可能是这样的:

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

还行。所以这是一个无限循环,检查按钮是否被点击,不检查其他任何内容。那么你期望浏览器如何更新 UI 并跟踪鼠标呢?这就引出了人们尝试做的下一件事:

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

好的,太好了。但有两个问题。首先,如果有人要编写像 Google Charts 或 jQuery 这样的库,或者任何与 UI 相关的内容,他们要么编写自己的循环,要么您必须手动将他们的函数复制/粘贴到 while 循环中。这无法扩展。其次,更重要的是,这是低效的。while 循环将使用 100% 的 CPU 时间检查按钮。如果浏览器可以告诉我们何时单击按钮,那不是更好吗?while(1)...

幸运的是,在 javascript 中,函数只是值。您可以将函数传递给浏览器,并告诉它在有人单击按钮时执行您的函数:

button.addEventListener('click', doSomething);

注意:请注意将函数视为变量和调用函数之间的区别。如果要将函数视为变量,只需使用名称即可。如果要调用函数,请使用大括号。doSomething()

为什么大家都坚持要写异步函数

每个人似乎都坚持制作异步 API,尤其是在 javascript 等语言中,有两个原因。

首先,低级文件和网络 I/O 是异步的。这意味着,如果要与数据库或服务器通信或读取文件,则需要将其实现为异步。此外,javascript 是单线程的。因此,如果使用 I/O 函数的同步版本,则将冻结其他所有内容。

其次,事实证明,在很多情况下(但肯定不是全部)异步,单线程编程与同步多线程编程一样快,有时甚至更快。

综合起来,上述两个原因在js社区中造成了社会压力,要求确保所有的I/O代码都是异步的,以保持速度优势,不阻塞其他人的代码。