asynchronous控制stream – 完成for循环后返回true

我在Node.js中有以下方法:

var foo = function(items){ for (var i=0; i<items.length; i++){ var item = items[i]; bar(item); } return true; } 

我想在所有酒吧完成更新后,foo返回true。 我怎么做?

编辑:我正在寻找一种方法来做到这一点,如果可能的话没有任何外部库。

有很多select来解决你想要的,你只需要定义你的funcions foobar的本质。 这里有一些选项:

您的原始代码不需要它:

你的foobar函数在它们的参数中没有任何callback ,所以它们不是asynchronous的。

Asuming foo函数是asynchronous的,bar函数是同步的:

你应该使用callback重写你的原始代码,如下所示:

 var foo = function(items, fooCallback){ for (var i=0; i<items.length; i++){ var item = items[i]; bar(item); } fooCallback(true); } 

使用Async:

你应该在开始安装它:

 npm install async 

你使用asynchronous ,像这样。

 var async = require('async'); var foo = function(items){ async.each(items, function(item, callback){ var _item = item; bar(_item); callback(); }, function(err){ return true; } }); } 

for循环的定义是“完成的”取决于bar是同步的还是asynchronous的。 如果bar是同步的 – 也许它正在做一些长时间运行的计算 – 那么当循环完成时你的代码已经返回了。

但是根据你说的问题的一部分

毕竟酒吧已经完成更新

这似乎是一个合理的假设, bar是asynchronous的 – 也许它正在进行networking调用。 但目前它没有任何机制来报告何时完成。 因此,第一个工作是重新定义bar ,使其具有asynchronous界面。 有两种基本types的asynchronous接口。 较古老的经典节点方法是callback。 一个新的方法是承诺。

因此,在任何情况下,您都需要以这种或那种方式重新定义bar 。 除非devise为以这种方式使用,否则不能asynchronous使用接口。

在我们给出了一个asynchronous接口之后,我们可以使用foo内的这个接口来正确地报告这些条是否“完全完成”。

callback

在callback的情况下,我们将bar的调用顺序改为bar(item, callback) ,给它一个返回的方法。 我们还需要在foo例程中进行callback,当所有的bar调用完成时调用。 按照你的喜好不要使用库,其逻辑可能是这样的:

 function foo(items, callback) { var count = items.length; var results = []; items.forEach(function(item, idx) { bar(item, function(err, data) { if (err) callback(err); results[idx] = data; if (!count--) callback(null, results); }); }); } 

这在项目上循环,为每个项目调用bar 。 当每个酒吧完成时,我们把它的结果放在一个results数组中,并检查这是否是最后一个酒吧,在这种情况下,我们称之为顶级callback。

这将被用作

 foo(items, function(err, data) { if (err) console.log("Error is", err); else console.log("All succeeded, with resulting array of ", data); }); 

上述基本上等同于使用async.each ,如另一个答案中所build议的,如下所示:

 function foo(items, callback) { async.each(items, bar, callback); } 

等待每个酒吧完成

如果您想在继续下一步之前等待每个小节完成,使用async ,每个系列都非常容易:

 function foo(items, callback) { async.eachSeries(items, bar, callback); } 

非库代码会更复杂一些:

 function foo(items, callback) { var results = []; function next(i) { if (i >= items.length) return callback(results)); bar(items[i], function barCallback(err, data) { if (err) return callback(err); results[i] = data; next(++i); }); } next(0); } 

在这里,每个小节完成的callback报告( barCallback )调用next例程,增加i来启动下一个小节的调用。

承诺

但是,你可能会更好地使用承诺。 在这种情况下,我们将devisebar来返回一个promise,而不是调用callback函数。 假设bar调用一些数据库例程MYDB ,新版本的bar可能看起来像这样:

 function bar(item) { return MYDB.findRecordPromise(item.param); } 

如果MYDB只提供老式的基于callback的接口,那么基本的方法是

 function bar(item) { return new Promise(function(resolve, reject) { MYDB.findRecord(item.param, function(err, data) { if (err) reject(err); else resolve(data); }); }); } 

现在我们有一个基于promise的版本,我们将定义foo来返回promise,而不是采用额外的callback参数。 那很简单:

 function foo(items) { return Promise.all(items.map(bar)); } 

Promise.all接受一系列的promise(我们通过bar的映射创builditems ),当它们全部满足时,用满足值的数组的值来实现,当它们中的任何一个被拒绝时,拒绝。

这用作

 foo(items) . then( function(data) { console.log("All succeeded, with resulting array of ", data); }, function(err) { console.log("Error is", err); } ); 

等待每个酒吧完成

如果用承诺的方法,你想等待每个酒吧完成下一个开始之前,那么基本的方法是:

 function foo(items) { return items.reduce(function(promise, item) { return promise.then(function() { return bar(item); }); }, Promise.resolve()); } 

这是以“空的”(预先解决的)承诺开始的,然后每次循环时,都会向链中添加一个,以便在前一个结束时执行下一个条。

如果您想要使用新的await关键字,请使用先进的asynchronous函数:

 async function foo(items) { for (var i = 0; i < items.length; i++) { await bar(items[i]); } } 

在旁边

顺便说一句,在另一个答案的假设,你可能正在寻找一个asynchronous接口到一个例程( foo ),它循环一些调用bar或者是同步的,或者可能是asynchronous的,但只能被踢出去,没有办法知道什么时候完成,看起来很奇怪。 考虑这个逻辑:

 var foo = function(items, fooCallback){ for (var i=0; i<items.length; i++){ var item = items[i]; bar(item); } fooCallback(true); } 

这与原来的代码没有任何区别,除了不是返回true,而是使用true调用callback。 它不能确保实际完成条形图,因为重复自己,没有给出一个新的asynchronous接口,我们无法知道它们已经完成。

使用async.each显示的代码存在类似的缺陷:

 var async = require('async'); var foo = function(items){ async.each(items, function(item, callback){ var _item = item; // USE OF _item SEEMS UNNECESSARY bar(_item); callback(); // BAR IS NOT COMPLETED HERE }, function(err){ return true; // THIS RETURN VALUE GOES NOWHERE }); } 

这只会启动所有的酒吧,但是在完成之前立即调用callback。 整个async.each部分将会或多或less地立即完成,之后立即调用最终function(err) 。 它返回的true值就会直接进入外层空间。 如果你想这样写,应该是

 var foo = function(items, fooCallback){ async.each(items, function(item, callback){ bar(item); callback(); }, function(err){ fooCallback(true); // CHANGE THIS } }); } 

但即使在这种情况下, fooCallback也会立即被调用,无论是否已完成或者是否返回错误。