2016-05-24 36 views
0

当涉及到JavaScript回调时,我非常困惑......特别是当试图在同一时间学习节点时。 您知道,我理解将函数作为参数传递给另一个函数的概念,但我最主要困惑的是预制模块“接受”回调函数。了解回调

例如一个简单的请求看起来像这样:

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个参数...请求模块如何知道使用返回的数据填充这些变量?

+1

imo,用JavaScript了解'回调'或关闭。通过node.js,尽快了解'promises'。并且只能在非常有限的情况下使用回调。或命令链可能有用?也许有趣? http://stackabuse.com/avoiding-callback-hell-in-node-js/ –

+0

@Query - 如果下面的答案之一回答了您的问题,请通过点击最佳答案左侧的绿色复选标记来指出。如果不是,那么请对每个答案评论它尚未回答的内容。 – jfriend00

回答

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假定回调接受三个参数我过去,理应不接受参数的方法。这工作,虽然三个参数,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将得到undefined内部的回调正文。

这两个例子都表明调用者并不在意你的回调是如何定义的,它的参数个数是否符合期望值。在任何不匹配的情况下,会发生什么最坏的是一个运行时错误:

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

get(1); 

// Uncaught TypeError: callback is not a function 
1

This get method accepts a url and a callback. Who defines that a callback is accepted? The developer of the specific method?

是。作者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); 
}); 

Also, the 3 parameters that we pass within the function (error, response, and body)... how does the request module know to populate these variables with data on return?

的参数给出,并且它们的顺序是request的定义的一部分,相同delay()上述限定(value)将是其callback参数(一个或多个)。

提供回调时,参数只给出您自己使用的参数名称。它们对主函数(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); 
}); 
1

函数/方法的设计者决定什么样的参数会接受和处理。所以,如果一个函数接受回调作为参数,那么函数的设计和实现就决定了它,并且会在适当的时候调用回调函数。

它也是函数/方法的实现者,它决定在调用时将传递给回调函数的参数。令许多人困惑的是,函数的调用者定义了将由主函数/方法调用的回调函数本身。但是回调函数的参数必须声明为与主函数/方法将传递给回调的内容相匹配。定义回调时为参数提供的标签仅仅是用于编程方便的标签(作为按名称引用参数的一种方式)。在回调函数中如何定义回调参数与实际传递给回调函数的内容无关 - 这一切都由调用回调的函数/方法决定。所以,当你使用request.get()时,使用它的合同是它接受一个回调,该回调应该被声明为具有参数(error, response, body),因为它将被调用。现在,如果你不打算使用它们(它们仍然会在那里,但你不会有一个方便的名称来引用它们),那么在最后抛弃一些论据是可以的,但是你不能因为命令很重要,所以在你打算使用任何打算使用之前,请将论点放开。

Who defines that a callback is accepted? The developer of the specific method?

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

Also, the 3 parameters that we pass within the function (error, response, and body)... how does the request module know to populate these variables with data on return?

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

例如,如果你宣布你的回调是:

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

然后,你的回调所认为的response实际上是error值,因为request.get()把误差值在第一个参数,无论怎样回调本身被声明。

看到此问题的另一种方法是定义没有定义参数的回调,然后使用arguments对象完全访问它们。

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

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