理解JavaScript回调与node.js的概念,特别是在循环中 [英] understanding the concept of javascript callbacks with node.js, especially in loops

查看:73
本文介绍了理解JavaScript回调与node.js的概念,特别是在循环中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚从node.js开始。我做了一些ajax东西,但没有太复杂,所以回调仍然是在我的头。我看了async,但我需要的是顺序运行几个函数。

I am just starting with node.js. I have done a little ajax stuff but nothing too complicated so callbacks are still kind of over my head. I looked at async, but all I need is to run a few functions sequentially.

我基本上有一些东西,从一个API拉一些JSON,创建一个新的,然后做一些事情。显然,我不能只是运行它,因为它一次运行所有的东西,并有一个空的JSON。大多数进程必须顺序运行,但如果从API中提取JSON,它可以拉动其他JSON,而它在等待,那是很好。我只是在把回调放在一个循环时感到困惑。我该如何处理索引?我想我已经看到一些地方使用循环中的回调作为一种递归函数,并不使用循环。

I basically have something that pulls some JSON from an API, creates a new one and then does something with that. Obviously, I can't just run it because it runs everything at once and has an empty JSON. Mostly the processes have to run sequentially, but if while pulling JSON from the API it can pull other JSON while it's waiting then that is fine. I just got confused when putting the callback in a loop. What do I do with the index? I think I have seen some places that use callbacks inside the loop as kind of a recursive function and don't use for loops at all.

简单的例子将有助于很多。

Simple examples would help a lot.

推荐答案

如果回调在同一作用域中定义,循环定义在(通常是这种情况)回调将有权访问索引变量。暂时忽略NodeJS详细信息,让我们考虑这个函数:

If the callback is defined in the same scope the loop is defined in (which is frequently the case), then the callback will have access to the index variable. Leaving aside NodeJS particulars for a moment, let's consider this function:

function doSomething(callback) {
    callback();
}

该函数接受一个回调函数引用,它所做的就是调用它。不是很令人兴奋。 : - )

That function accepts a callback function reference and all it does is call it. Not very exciting. :-)

现在让我们在循环中使用:

Now let's use that in a loop:

var index;

for (index = 0; index < 3; ++index) {
    doSomething(function() {
        console.log("index = " + index);
    });
}

(在计算密集型代码中— ;

(In compute-intensive code — like a server process — best not to literally do the above in production code, we'll come back to that in a moment.)

现在,当我们运行时我们看到预期的输出:

Now, when we run that, we see the expected output:

index = 0
index = 1
index = 2

我们的回调函数可以访问 index ,因为回调是对定义的范围内的数据的闭包。 (不要担心术语关闭,关闭不复杂 。)

Our callback was able to access index, because the callback is a closure over the data in scope where it's defined. (Don't worry about the term "closure," closures are not complicated.)

我说的原因可能是最好不要在计算密集型生产代码中做上面的是代码在每次迭代创建一个函数(在编译器中禁止花哨的优化,V8非常聪明,但是优化创建这些函数是非常简单的)。下面是一个稍微修改的例子:

The reason I said it's probably best not to literally do the above in compute-intensive production code is that the code creates a function on every iteration (barring fancy optimization in the compiler, and V8 is very clever, but optimizing out creating those functions is non-trivial). So here's a slightly reworked example:

var index;

for (index = 0; index < 3; ++index) {
    doSomething(doSomethingCallback);
}

function doSomethingCallback() {
    console.log("index = " + index);
}

这看起来有点令人惊讶,但它仍然工作方式相同,仍然具有相同的输出,因为 doSomethingCallback 仍然是 index 的闭包,因此它仍然看到 index 。但现在只有一个 doSomethingCallback 函数,而不是每个循环上的一个新的。

This may look a bit surprising, but it still works the same way, and still has the same output, because doSomethingCallback is still a closure over index, so it still sees the value of index as of when it's called. But now there's only one doSomethingCallback function, rather than a fresh one on every loop.

现在让我们取负例如,工作:

Now let's take a negative example, something that doesn't work:

foo();

function foo() {
    var index;

    for (index = 0; index < 3; ++index) {
        doSomething(myCallback);
    }
}

function myCallback() {
    console.log("index = " + index); // <== Error
}

失败,因为 index 定义在中,因此未定义在同一作用域(或嵌套作用域)

That fails, because myCallback is not defined in the same scope (or a nested scope) that index is in defined in, and so index is undefined within myCallback.

最后,让我们考虑在中设置事件处理程序一个循环,因为一个人必须小心。这里我们将深入NodeJS:

Finally, let's consider setting up event handlers in a loop, because one has to be careful with that. Here we will dive into NodeJS a bit:

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
    });
}

似乎方式,我们早期的循环,但有一个关键的区别。在我们早期的循环中,回调被立即调用,因此它看到正确的 index 值,因为 index hadn' t有机会移动。在上面,虽然,我们将在调用回调之前循环。结果?我们看到

It seems like the above should work the same way that our earlier loops did, but there's a crucial difference. In our earlier loops, the callback was being called immediately, and so it saw the correct index value because index hadn't had a chance to move on yet. In the above, though, we're going to spin through the loop before the callback is called. The result? We see

Process index 3 exited
Process index 3 exited
Process index 3 exited

这是一个关键点。闭包没有其关闭的数据的副本,它有一个实时引用。因此,当每个进程的 exit 回调运行时,循环已经完成,因此所有三个调用都会看到相同的 index value(它的值为 end )。

This is a crucial point. A closure doesn't have a copy of the data it closes over, it has a live reference to it. So by the time the exit callback on each of those processes gets run, the loop will already be complete, so all three calls see the same index value (its value as of the end of the loop).

我们可以通过回调使用一个不会改变的不同的变量,例如:

We can fix this by having the callback use a different variable that won't change, like this:

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");
    };
}



现在我们输出正确的值(以进程退出的顺序):

Now we output the correct values (in whatever order the processes exit):

Process index 1 exited
Process index 2 exited
Process index 0 exited

方法是,我们分配给 exit 事件的回调关闭在调用 i 参数 makeExitCallback 。第一个回调 makeExitCallback 创建并返回 i 的值 makeExitCallback c> $ ,它创建的第二个回调会关闭 i / code>(这不同于较早调用的 i 值)等。

The way that works is that the callback we assign to the exit event closes over the i argument in the call we make to makeExitCallback. The first callback that makeExitCallback creates and returns closes over the i value for that call to makeExitCallback, the second callback it creates closes over the i value for that call to makeExitCallback (which is different than the i value for the earlier call), etc.

如果您提供上面链接的文章阅读,那么一些事情应该更清晰。文章中的术语有点过时(ECMAScript 5使用更新的术语),但概念没有改变。

If you give the article linked above a read, a number of things should be clearer. The terminology in the article is a bit dated (ECMAScript 5 uses updated terminology), but the concepts haven't changed.

这篇关于理解JavaScript回调与node.js的概念,特别是在循环中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆