我理解回调函数背后的概念如下:作为参数传递给另一个函数的函数。其背后的想法是,当事件“A”发生时,函数“A”可以使用函数“B”,但在此之前代码仍然可以正常运行,而不是等待事件“A”。我不明白的是一些语法以及代码实际如何使其工作。
所以使用这样的代码:
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(8080);
console.log('Server running on port 8080.');
我理解函数(req,res)部分是执行回调函数的“匿名函数”。但我不知道如何以及为什么。为什么关键字要执行此“功能”,这些参数来自哪里?我仍然没有找到一个很好的解释这是如何工作的。我在高度抽象的隐喻层面得到了它的工作原理,但我不明白代码的含义。
答案 0 :(得分:0)
首先,让我们取出匿名函数并使其成为常规函数:
var http = require('http');
function serverCallback(req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}
http.createServer(serverCallback).listen(8080);
在这里你可以看到代码正在调用http.createServer()
并传递一个参数,这是一个函数引用。
根据http.createServer()
的文档,当它调用该回调时,它会将两个参数传递给该函数。第一个参数是request
对象,第二个参数是response
对象。因此,当我们声明我们的回调函数时,我们声明并将这两个参数命名为回调,以便我们可以正确使用发送给它的参数。
与Javascript回调混淆的一个共同点是,http.createServer()
函数决定传递此回调的参数,我们必须声明我们的回调参数以匹配主机函数将传递给它的内容。这些参数名称只是方便的编程名称,我们可以用来引用这两个参数 - 我们称之为不会影响实际传递的内容。因此,我们必须选择与http.createServer()
函数实际传递给回调的名称相匹配的名称。
所以,既然您已经了解了基本回调的工作原理,那么命名函数serverCallback
可以像这样内联:
var http = require('http');
http.createServer(function serverCallback(req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(8080);
我实际上只是采用了serverCallback的最高定义并将其置于内联中。注意,它甚至还有函数名称。这也可以正常工作。但是,我们并没有真正使用serverCallback
名称,因此不需要它。这可以删除,最终得到这个:
var http = require('http');
http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(8080);
现在这是一个内联匿名函数声明。它与常规函数声明相同,它只是声明为内联,函数名称是可选的。
现在,针对您的具体问题:
据我所知,函数(req,res)部分是“匿名的” 函数“这是执行回调函数的原因。但是我 不知道怎么做和为什么。为什么关键字要执行此“功能”,以及 这些参数来自哪里?
function
是你如何声明一个函数。这是内联函数声明,因此仍然需要function
关键字。参数在我上面的解释中描述。它们被声明为我们在代码中使用的方便的标记,以匹配http.createServer()
在调用它时传递回调的内容。 req
是一个请求对象,res
是一个响应对象,并且在node.js文档中都有自己的页面,因为它们具有属性和方法。
我仍然没有找到一个很好的解释这是如何工作的。我在高度抽象的隐喻层面得到了它的工作原理,但我不明白代码的含义。
如果我的上述说明为您提供有关代码如何运作的任何进一步问题,请告诉我。
我不明白的是一些语法和代码如何 实际上是让它发挥作用。
如果,您在这里问的是,每当某个服务器事件发生时,Javascript如何调用此回调,那么这是一个更长的对话。 http.createServer()
后面是一些设置服务器的本机代码。在TCP级别,它定义正在侦听新连接的传入端口。当其中一个新连接发生时(来自外部的某个人建立与您的服务器的连接),OS服务将通知此http服务器后面的本机代码已建立新的传入连接(例如,现在已连接新的TCP套接字并且已通过该套接字发送HTTP请求)。然后,此后面的本机代码将向Javascript事件队列添加事件和一些数据。
如果当前的JS解释器当前没有运行,那么向事件队列添加内容将触发该事件的回调,它将创建适当的参数并调用关联的回调,执行您注册为回调的任何代码。如果JS引擎当前正在运行其他一些JS代码,那么该事件将位于JS事件队列中,直到当前的JS线程完成执行。此时,JS引擎将把下一个事件从事件队列中拉出并执行它(调用它的回调)。
有关事件队列的一般概念的更多信息,您可以阅读以下其他帖子:
How does JavaScript handle AJAX responses in the background?
答案 1 :(得分:0)
req
和res
参数(请求和响应)在节点HTTP Server module.中定义
让我们简化一下并制作我们自己的回调函数:(不是匿名函数,所以应该更容易理解)
function first(callback) {
console.log("First!");
callback();
}
function second() {
console.log("Second!");
}
first(second);
应输出到控制台:首先!第二!
答案 2 :(得分:0)
JavaScript中的函数是一个Object。因此,您可以这样对待并传递它。除非你告诉它,它不会立即执行。除非你告诉它,否则它根本不会执行。
所以,如果我有一个抓狗玩具的功能:
function fetchDogToy() {
trackToy();
runTowardsToy();
pickUpToy();
}
我有一个让我回到玩具的功能:
function returnToyToOwner() {
runTowardsOwner();
dropToy();
}
在returnToyToOwner
完成之前,我可能不希望pickUpToy
执行。因此,我会将returnToyToOwner
传递给fetchDogToy
,因此我可以指定 我想要执行它。在我能做到这一点之前,我需要指定fetchDogToy
能够接受一个参数,但我们知道这将是一个函数对象,所以只需相应地命名:
function fetchDogToy(callbackFunction) {
trackToy();
runTowardsToy();
pickUpToy();
}
现在,我们可以传入一个参数。我们知道这将是一个函数对象,因此,让我们在它们希望它执行时告诉它:
function fetchDogToy(callbackFunction) {
trackToy();
runTowardsToy();
pickUpToy();
callbackFunction(); // executed the function object after toy is picked up!
}
所以,这是fetchDogToy
的设置。我们可以称之为两种不同的方式。一种方法是在致电returnToyToOwner
时直接传递fetchDogToy
:
fetchDogToy(returnToyToOwner);
但是,在整个应用程序中不同情况下玩具被拾取后,您可能需要fetchDogToy
的不同行为,因此我们通常只是按原样编写原始函数对象并以这种方式传递:
fetchDogToy(function returnToyToOwner() {
runTowardsOwner();
dropToy();
});
它可以命名为function,或匿名:
fetchDogToy(function() {
runTowardsOwner();
dropToy();
});
希望这有帮助!
旁注:暂时考虑fetchDogToy
是您在某些API在线文档中阅读的第三方库函数。您现在可以看到,如果他们希望您将函数对象作为回调传递,那么您真的不知道何时他们将调用您传递的代码,除非您自己阅读源代码。