JavaScript:回调函数如何工作?

时间:2017-03-15 15:24:57

标签: javascript

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

我该怎么做呢?

5 个答案:

答案 0 :(得分:0)

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

如果你想在使用回调函数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])
      }
  }
 }

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

答案 1 :(得分:0)

你已经在那里了。您的代码几乎完全正确。 你刚刚缺少功能键工作声明。

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

&#13;
&#13;
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);
&#13;
&#13;
&#13;

答案 2 :(得分:0)

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

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采用称为“回调”的参数。完成insertNumbers后,我们只需运行callback()。在对insertNumber的初始调用中,我们传递一个函数作为参数,一旦insertNumers完成(或调用callback()),就会执行该函数。

答案 3 :(得分:0)

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

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

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

当某些事情需要花费任意时间时(例如从磁盘读取数据或从网络请求数据)时,会使用回调。

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

setTimeout(myCallback, 5000)

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

因此,正如您所看到的,我可以使用我的函数myCallback并将其提供给另一个函数(在此实例中为setTimeout),以便在另一个函数完成任务后使用。

答案 4 :(得分:0)

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

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

几乎所有编程语言都可以处理值。因此,如果您已经完成了任何编程,那么您就可以了解哪些值是基本的(我将在这里大大简化值的类型,并将值和引用/指针引用为&#34 ;值&#34;。)

价值是一种东西。例如数字或字符串。因此22.31是一个值,"Hello Dave"是一个值。

大多数语言也有变量的概念(尽管不是这样)。变量是&#34;名称&#34;我们赋予值以便更容易处理值。例如,在下文中,x是一个变量:

var x = 12;

..它的值是12。

变量允许我们做什么?它允许我们在计算中用值替换名称。就像数学一样。例如,如果x12且我们知道我们可以将1添加到12,我们也可以这样做:

x + 1

函数是值

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

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

由于变量的作用是允许您使用名称替换值,因此如果您可以使用语法a调用函数a(),则意味着您也可以使用变量{{ 1}}:

y

回调

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

y(); // returns "Hello"

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

function a () {return "Hello"}

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

b(); // returns "Hello World"

如你所见。回调并不特别。它们只是因为javascript函数遵循与其他值(如数字,数组和字符串)相同的规则这一事实的结果。

回调并不意味着异步

从上面的示例中可以看出,对函数function a () {return "Hello"} function b () {return "Bye"} function c (functionVariable) {return functionVariable() + " World"} c(a); // returns "Hello World" c(b); // returns "Bye World" 的两次调用都返回一个值。因此,函数c即使接受回调也不是异步的。因此,回调可以用于同步和异步代码。

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

为什么异步代码需要回调

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

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

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

Array.sort()

行。这是一个无限循环,检查按钮是否被点击而没有其他内容。那么您如何期望浏览器更新UI并跟踪鼠标?这引导我们接下来的人们试图做的事情:

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

行。大。但是有两个问题。首先,如果有人要编写像Google Charts或jQuery这样的库或者与UI有关的任何内容,他们要么编写自己的while (1) { if (button.isClicked) { doSomething(); } else { updateUI(); } } 循环,要么必须手动将其功能复制/粘贴到while循环中。这不规模。其次,更重要的是,这是低效的。 while循环将使用100%CPU时间检查按钮。如果浏览器可以告诉我们何时点击按钮,那么它会不会更好。

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

while(1)...
  

注意:注意将函数视为变量和调用函数之间的区别。如果要将函数视为变量,只需使用名称即可。如果要调用函数,请使用button.addEventListener('click', doSomething);

等大括号

为什么每个人都坚持编写异步函数

有两个原因可以解释为什么每个人都坚持制作异步API,尤其是在javascript等语言中。

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

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

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