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 foo
和bar
的本质。 这里有一些选项:
您的原始代码不需要它:
你的foo
和bar
函数在它们的参数中没有任何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
也会立即被调用,无论是否已完成或者是否返回错误。