Mr *_*SON 31 javascript callback node.js
我刚刚开始使用node.js. 我做了一些ajax的东西,但没有太复杂,所以回调仍然是我的头脑.我看了异步,但我需要的是按顺序运行一些函数.
我基本上有一些东西从API中提取一些JSON,创建一个新的,然后用它做一些事情.显然,我不能只运行它,因为它一次运行所有内容并且有一个空的JSON.大多数进程必须按顺序运行,但是如果从API中提取JSON,它可以在等待时拉出其他JSON然后就可以了.我把回调放在一个循环中时感到很困惑.我该怎么处理索引?我想我已经看到一些在循环中使用回调的地方作为一种递归函数,并且根本不使用for循环.
简单的例子会有很大帮助.
T.J*_*der 86
如果回调是在相同的范围内定义的,则定义循环(通常是这种情况),然后回调将有权访问索引变量.暂且不谈NodeJS细节,让我们考虑一下这个功能:
function doSomething(callback) {
callback();
}
Run Code Online (Sandbox Code Playgroud)
该函数接受回调函数引用,它所做的就是调用它.不是很令人兴奋.:-)
现在让我们在循环中使用它:
var index;
for (index = 0; index < 3; ++index) {
doSomething(function() {
console.log("index = " + index);
});
}
Run Code Online (Sandbox Code Playgroud)
(在计算密集型代码中 - 就像服务器进程一样 - 最好不要在生产代码中按字面意思执行上述操作,我们马上回过头来看.)
现在,当我们运行它时,我们看到了预期的输出:
index = 0
index = 1
index = 2
Run Code Online (Sandbox Code Playgroud)
我们的回调能够访问index,因为回调是对其定义范围内的数据的闭包.(不要担心术语"关闭",关闭并不复杂.)
我之所以说最好不要在计算密集型生产代码中完成上述操作,原因是代码在每次迭代时都会创建一个函数(除非在编译器中进行花哨的优化,而V8非常聪明,但优化创建这些函数是非平凡).所以这是一个稍微重做的例子:
var index;
for (index = 0; index < 3; ++index) {
doSomething(doSomethingCallback);
}
function doSomethingCallback() {
console.log("index = " + index);
}
Run Code Online (Sandbox Code Playgroud)
这可能看起来有点令人惊讶,但它仍然以相同的方式工作,并且仍然具有相同的输出,因为doSomethingCallback它仍然是一个闭包index,所以它仍然可以看到index它被调用时的值.但现在只有一个doSomethingCallback功能,而不是每个循环上的新功能.
现在让我们采取一个反面的例子,这是不起作用的:
foo();
function foo() {
var index;
for (index = 0; index < 3; ++index) {
doSomething(myCallback);
}
}
function myCallback() {
console.log("index = " + index); // <== Error
}
Run Code Online (Sandbox Code Playgroud)
这失败了,因为myCallback没有在定义的同一范围(或嵌套范围)index中定义,因此index未在其中定义myCallback.
最后,让我们考虑在循环中设置事件处理程序,因为必须要小心.在这里,我们将深入研究NodeJS:
var spawn = require('child_process').spawn;
var commands = [
{cmd: 'ls', args: ['-lh', '/etc' ]},
{cmd: 'ls', args: ['-lh', '/usr' ]},
{cmd: 'ls', args: ['-lh', '/home']}
];
var index, command, child;
for (index = 0; index < commands.length; ++index) {
command = commands[index];
child = spawn(command.cmd, command.args);
child.on('exit', function() {
console.log("Process index " + index + " exited"); // <== WRONG
});
}
Run Code Online (Sandbox Code Playgroud)
它看起来像上面应该工作相同的方式,我们前面的循环一样,但有一个关键的区别.在我们之前的循环中,回调被立即调用,因此它看到了正确的index值,因为index还没有机会继续前进.但是,在上面,我们将在调用回调之前旋转循环.结果?我们看
Process index 3 exited
Process index 3 exited
Process index 3 exited
Run Code Online (Sandbox Code Playgroud)
这是至关重要的一点.闭包没有它关闭的数据的副本,它有一个实时引用.因此,当exit每个进程的回调运行时,循环将已经完成,因此所有三个调用都看到相同的index值(它的值与循环结束时相同).
我们可以通过让回调使用一个不会改变的不同变量来解决这个问题,如下所示:
var spawn = require('child_process').spawn;
var commands = [
{cmd: 'ls', args: ['-lh', '/etc' ]},
{cmd: 'ls', args: ['-lh', '/usr' ]},
{cmd: 'ls', args: ['-lh', '/home']}
];
var index, command, child;
for (index = 0; index < commands.length; ++index) {
command = commands[index];
child = spawn(command.cmd, command.args);
child.on('exit', makeExitCallback(index));
}
function makeExitCallback(i) {
return function() {
console.log("Process index " + i + " exited");
};
}
Run Code Online (Sandbox Code Playgroud)
现在我们输出正确的值(以进程退出的顺序):
Process index 1 exited
Process index 2 exited
Process index 0 exited
Run Code Online (Sandbox Code Playgroud)
有效的方法是我们分配给exit事件的回调将关闭i我们调用的调用中的参数makeExitCallback.makeExitCallback创建和返回的第一个回调将关闭i该调用的值,makeExitCallback它创建的第二个回调将关闭该调用的i值(不同于之前调用的值),等等.makeExitCallbacki
如果您将上面链接的文章作为阅读,那么应该更清楚一些事情.文章中的术语有点过时(ECMAScript 5使用更新的术语),但概念没有改变.
| 归档时间: |
|
| 查看次数: |
17220 次 |
| 最近记录: |