当前位置: 首页 > 面试题库 >

了解使用node.js进行JavaScript回调的概念,尤其是在循环中

禹昊穹
2023-03-14
问题内容

我只是从node.js开始。我做了一些ajax的工作,但是没有什么太复杂的,所以回调仍然有点麻烦。我看了看异步,但我所需要的只是顺序运行一些功能。

我基本上有一些东西可以从API中提取一些JSON,然后创建一个新的JSON,然后对此进行处理。显然,我不能只运行它,因为它可以一次运行所有内容并且具有空的JSON。通常,流程必须按顺序运行,但是如果在从API中提取JSON时可以在等待时提取其他JSON,那很好。将回调置于循环中时,我只是感到困惑。我该如何处理索引?我想我已经看到一些地方在循环中使用回调作为递归函数,而根本不使用循环。

简单的例子会很有帮助。


问题答案:

如果在与循环定义相同的作用域中定义了回调(通常是这种情况),则回调将有权访问index变量。暂时搁置NodeJS的细节,让我们考虑一下这个函数:

function doSomething(callback) {
    callback();
}

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

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

var index;

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

(在诸如服务器进程之类的计算密集型代码中,最好不要在生产代码中按字面意思进行上述操作,我们稍后再讲。)

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

index = 0
index = 1
index = 2

我们的回调能够访问index,因为回调是对定义了作用域的数据封闭 。(不要担心术语“
closure”,闭包并不复杂。)

我说过最好不要在计算密集型生产代码中执行上述操作,原因是该代码会在 每次迭代
创建一个函数(除非在编译器中进行了花哨的优化,而V8非常聪明,但通过创建这些函数来进行优化是可以的)不平凡的)。因此,这是一个稍作修改的示例:

var index;

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

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

这可能看起来有点令人惊讶,但是它仍然以相同的方式工作,并且仍然具有相同的输出,因为doSomethingCallback它仍然是over的闭包index,因此index在调用时仍能看到as
的值。但是现在只有一个doSomethingCallback函数,而不是每个循环都有一个新函数。

现在让我们举一个负面的例子,那 行不通的:

foo();

function foo() {
    var index;

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

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

之所以失败,是因为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
    });
}

看起来
像上面应该工作相同的方式,我们前面的循环一样,但有一个关键的区别。在我们之前的循环中,回调被立即调用,因此它看到了正确的index值,因为index还没有机会继续前进。不过,在上面的代码中,我们将在调用回调之前旋转整个循环。结果?我们看

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

这是关键点。闭包没有其关闭的数据的 副本 ,它具有对其的 实时引用
。因此,当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");
    };
}

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

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

起作用的方式是,我们分配给exit事件的回调i在对的调用中的参数上关闭makeExitCallback。第一回调makeExitCallback创建并返回关闭在i该呼叫到值makeExitCallback,它在创建关闭第二回调i为值
呼叫makeExitCallback(这是比不同i的先前调用值)等。

如果您对上面链接的文章进行了阅读,那么很多事情都应该更加清楚。本文中的术语有些过时(ECMAScript 5使用更新的术语),但是概念没有改变。



 类似资料:
  • 问题内容: 我是一个长期的PHP(CodeIgniter和WordPress)开发人员,直到最近才想学习其他几种语言。我已经着手通过node.js学习Ruby(在Rails和Sinatra上),Python(在Flask框架下)和Javascript。 我决定使用每种语言创建一个我能想到的最基本的应用程序,即URL扩展器。除了node.js和Javascript,我已经设法用每种语言创建了一个工作

  • 问题内容: 我需要有关node.js异步特性的帮助。我有一个for循环,该循环从数据库收集数据。“结果”是一个数组,然后应将其返回到主函数。 如何确保循环完成后执行回调? 问题答案: 您可能要考虑使用帮助程序库,例如 异步 https://github.com/caolan/async 它有助于使代码更加一致。 就您而言,您可以查看forEach()方法 调用迭代器时,将使用列表中的项目以及完成时

  • 问题内容: 我在使用redis和nodejs时遇到问题。我必须遍历电话号码列表,并检查我的Redis数据库中是否存在该号码。这是我的代码: “之后”控制台日志出现在“之前”控制台日志之前,并且回调始终返回一个empty 。这是因为如果我很了解,对redis的请求是异步的。但问题是我不知道如何使它起作用。我能怎么做 ? 问题答案: 您有两个主要问题。 您的变量将不是您想要的变量。可以通过更改 数组的

  • 什么是回调? 回调是函数的异步等价物。 在给定任务完成时调用回调函数。 Node大量使用回调。 Node的所有API都以支持回调的方式编写。 例如,读取文件的功能可以开始读取文件并立即将控制返回到执行环境,以便可以执行下一条指令。 一旦文件I/O完成,它将在传递回调函数时调用回调函数,该文件的内容作为参数。 因此没有阻塞或等待文件I/O. 这使得Node.js具有高度可扩展性,因为它可以处理大量请

  • 问题内容: 我是JavaScript和node.js的新手。我想遍历目录并将所有文件统计信息(而不是其他目录)添加到数组中。如下所示,我的代码存在问题,因为回调可能在for循环完成后被调用,因此在回调方法中使用“ i”变量将不起作用。但是代码应如何显示,以便以下代码段起作用?它与闭包有关吗? 感谢帮助! 问题答案: 您需要使用闭包是正确的。您应该将循环的内容包装在一个自调用函数中,以保留每次迭代的

  • 问题内容: 我试图用NodeJS编写代码,从外部API抓取数据,然后使用Mongoose在MongoDB中填充它们。在这之间,我将检查该特定对象是否已经存在于Mongo中。下面是我的代码。 我的问题是,由于NodeJS回调是并行的,因此不会按顺序调用它。我的最终结果将是这样的: 呼叫报告API console.log(长度)= 100 ^^^^^^^^^^^^^^^^^^^^^^^^^ conso