将数据传递给node.js回调——异步问题

Passing data into node.js callbacks - the asynchronous issue

本文关键字:回调 异步 问题 js node 数据      更新时间:2023-09-26

以下面的代码为例,其中用一些值初始化对象data,然后由一些时间密集型函数(如数据库访问)处理这些值。如果执行成功,则将成功的data项的名称打印到控制台中。否则,打印失败通知:

data = {first:  'someinfo',   second:  'somemoreinfo',   third:  'evenmoreinfo'};
for (var item in data) {
    timeIntensiveFunction(item, data[item], function(err) {
        if (!err) {
            console.log(item + ' processed successfully');
        } else {
            console.log(item + ' failed');
        }
    });
}

您希望控制台显示这个,假设函数对所有三个数据项都成功:

first processed successfully
second processed successfully
third processed successfully

相反,它将显示,假设第一次数据库访问时间比for循环长:

third processed successfully
third processed successfully
third processed successfully

这是因为控制台日志记录是在回调中完成的,只有在for循环完成之后才合理地调用,因为timeIntensiveFunction()需要很长时间。当第一个回调被调用时,item已经有了它的最后一个值third

如何将item的"当前"值传递给回调函数?

问题是因为它只调用最后一项的回调。

你可以用下面的函数绑定你的每一个项目。

var printStatus = function(item){
    return function(err) {
        if (!err) {
            console.log(item + ' processed successfully');
        } else {
            console.log(item + ' failed');
        }
    }
}
for (var item in data) {
    timeIntensiveFunction(item, data[item], printStatus(item));
}

这是javascript中闭包的一个常见问题。解决这个问题的一种方法是将函数调用包装在一个匿名函数中,并作用域为item。像这样:

for (var item in data) {
    (function(item){
        timeIntensiveFunction(item, data[item], function(err) {
           if (!err) {
               console.log(item + ' processed successfully');
           } else {
               console.log(item + ' failed');
           }
        });
   })(item);
}

如果你正在寻找一个可以更容易地处理异步任务的库,请查看caolan/async.

var async = require("async");
var data = [{id: "first"}, {id: "second"}, {id: "third"}];
function timeIntensiveFunction(item, done) {
  // do something
  console.log("time intensive task started:", item.id);
  // err?
  // if (err) return done(err);
  done();
}
function processItem(item, done) {
  timeIntensiveFunction(item, function(err) {
    if (err) return done(err);
    console.log("task complete:", item.id);
    done();
  });
}
async.map(data, processItem);

输出
time intensive task started: first
task complete: first
time intensive task started: second
task complete: second
time intensive task started: third
task complete: third

对于希望了解如何在没有库的情况下完成此操作的用户,您可以查看此答案的修订历史。