了解回调

时间:2016-05-24 20:49:21

标签: node.js

当谈到javascript回调时我很困惑...尤其是在尝试同时学习节点时。 你知道,我理解将一个函数作为参数传递给另一个函数的概念,但让我感到困惑的主要是预制模块,它们接受了#34;回调函数。

例如,一个简单的请求如下:

var request = require('request');
var url = 'http://www.google.com';

request.get(url, function(error, response, body) {

    if(error){
      console.log('This request resulted in an error', error);
    }
    console.log(body);
});

此get方法接受url和回调。 谁定义了回调被接受?具体方法的开发者? 此外,我们在函数内传递的3个参数(错误,响应和正文)...请求模块如何知道在返回时使用数据填充这些变量?

3 个答案:

答案 0 :(得分:1)

想象一下,get或多或少地实现了

function get(url, callback) {
    var response = they somehow retrieve the response;
    var body = they somehow parse the body;
    var error = ...;

    // they call you back using your callback
    callback( error, response, body );
}

如何计算这些值是无关紧要的(这是他们的实际工作)但是他们只是回调你,假设你的函数实际上接受了给定顺序的三个参数(如果不是你只是得到运行时错误)。

从这个意义上说,他们也有点接受"你的回调是......好吧,回调它:)不用担心参数的数量,因为Javascript非常宽容" - 可以使用任意数量的参数调用方法,包括比函数期望的更少或更多的参数。

以此为例:

// they provide 3 arguments
function get( callback ) {
   callback( 0, 1, 2 );
}

// but you seem to expect none
get( function() {
});

get假设回调接受三个参数,而我传递的方法据说不接受 less 参数。这是有效的,虽然三个参数012没有绑定到任何正式的回调参数(你不能直接引用它们),它们在那里arguments对象,类似于数组的结构:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments

再举一个例子

function get( callback ) {
   callback( 0, 1, 2 );
}

get( function(a,b,c,d,e) {
});

这一次我通过的回调似乎比调用者提供的更多参数更多,但这里没问题。参数绑定到连续的调用参数,a将获得0,b将获得1,c将获得2而de将获得{{ 1}}在回调体内。

这两个例子都表明,调用者并不关心你的回调是如何定义的,无论它的参数数量是否符合预期。如果出现任何不匹配,可能发生的最坏情况是运行时错误:

undefined

答案 1 :(得分:1)

  

此get方法接受url和回调。谁定义了回调被接受?具体方法的开发者?

是。 request.get()的作者专门定义它以期望并使用"回调"功能

您也可以定义自己的函数来接受回调,当它们依赖于其他异步函数时尤其有用 - 例如setTimeout()的简单示例。

function delay(value, callback) {
    setTimeout(function () {
        // this statement defines the arguments given to the callback
        // it specifies that only one is given, which is the `value`
        callback(value);
    }, 1000);
}

delay('foo', function (value) { // named to match the argument `delay` provides
    console.log(value);
});
  

此外,我们在函数中传递的3个参数(错误,响应和正文)...请求模块如何知道在返回时使用数据填充这些变量?

给出的参数及其顺序是request定义的一部分,与上面的delay()相同,定义(value)将是其callback的参数1}}。

提供回调时,参数仅提供您自己使用的参数名称。它们对主函数(request.get()delay()等)的行为没有影响。

即使没有命名任何参数,request.get()仍然以相同的顺序将相同的3个参数传递给回调:

request.get(url, function () { // no named parameters
    var error = arguments[0];  // still the 1st argument at `0` index
    var body = arguments[2];   // still the 3rd argument at `2` index

    if(error){
        console.log('This request resulted in an error', error);
    }
    console.log(body);
});

答案 2 :(得分:1)

函数/方法的设计者决定它将接受和处理的参数。因此,如果函数接受回调作为参数,那么函数的设计和实现决定了它并在适当的时候调用回调。

它也是函数/方法的实现者,它决定在调用时将哪些参数传递给回调。令许多人感到困惑的是,函数的调用者定义了将由main函数/方法调用的回调函数本身。但是必须声明回调函数的参数以匹配main函数/方法将传递给回调的函数。定义回调时给定参数的标签仅仅是编程方便的标签(作为通过名称引用参数的一种方式)。如何在回调函数中定义回调的参数与实际传递给回调的内容无关 - 这全部由调用回调的函数/方法决定。因此,当您使用request.get()时,使用它的合同是它接受一个应该被声明为具有参数(error, response, body)的回调,因为这是它将被调用的内容。现在,如果你不打算使用它们,可以在最后留下参数(它们仍然会存在,但你只是没有方便的名称来引用它们),但是你不能在您打算使用的任何内容之前不要使用参数,因为顺序很重要。

  

谁定义接受回调?特定的开发人员   方法

是的,函数/方法的开发人员决定是否接受回调。

  

此外,我们在函数中传递的3个参数(错误,   响应和正文)...请求模块如何知道填充   这些变量带有返回数据吗?

request.get()方法被编程为将三个参数(error, response, body)传递给它的回调,这就是它将传递的内容。无论回调本身如何实际声明,request.get()将按顺序传递这三个参数。因此,让许多人感到困惑的是函数/方法的调用者自己创建了回调函数,并为它定义了参数。好吧,不是真的。回调的创建者必须创建一个回调,匹配函数/方法期望的契约。如果它与合同不匹配,则回调可能无法正常运行,因为它会对传递的内容感到困惑。这里的合同真的是回调的第一个参数是error值,如果该值不是真的,那么接下来的两个参数将是responsebody。您的回调必须与该联系人匹配才能正确使用这些参数。因为Javascript没有严格类型,如果你错误地声明你的回调,它将不会是一个运行时错误,但你对这些参数的访问可能是错误的 - 导致事情工作不正确。

例如,如果您将回调声明为:

function myCallback(response, body, err) { .... }

然后,你的回调看到的response实际上是error值,因为request.get()将错误值放在第一个参数中,无论回调本身是如何声明的。 / p>

另一种看待这种情况的方法是定义一个没有定义参数的回调,然后使用arguments对象完全访问它们。

function myCallback() {
    console.log(arguments[0]);    // err value
    console.log(arguments[1]);    // response value
    console.log(arguments[2]);    // body value
}

回调中的返回值也是如此。如果您使用Array.prototye.map(),它需要一个传递三个参数的回调并返回一个新的数组元素。即使您正在实现回调,也必须定义与该合同兼容的回调。