在 node.js 中迭代大量异步调用/结果(使用 ES6/异步/蓝鸟/生成器)?
Iterate large number of async calls / results in node.js (using ES6 / async / bluebird / generators)?
我正在 node.js 中编写一个实用程序,它必须每晚处理和连接大量文件。在同步伪代码中,它看起来像这样(为清楚起见省略了 try / catch):
while (true) {
var next = db.popNext();
if (!next) return;
out.append(next);
}
然而,在我使用的库中 popNext()
实际上是一个节点风格的异步方法,看起来像这样:popNext(callback)
.
由于我是从头开始编写中间件,所以我可以使用 --harmony
(例如,generators), async or bluebird。
理想情况下,我更喜欢这样的东西:
forEachOrdered(db.popNext, (error, next, ok, fail) => {
if(error) return; // skip
// If there was an internal error, terminate the whole loop.
if(out.append(next)) ok();
else fail();
}).then(() => {
// All went fine.
}).catch(e => {
// Fail was called.
});
但是,我对其他 'standard' 解决方案持开放态度。我想知道这个问题最简洁的解决方案是什么?
Edit 仅同时生成所有(在常规 for 循环中)可能无法解决我的问题,因为我们谈论的是 100k,对于我必须的每个项目打开并读取一个文件,所以我可能 运行 没有文件描述符。
这是一个使用 bluebird 协程的解决方案,使用您的 "ideal" 代码:
var db = Promise.promisifyAll(db);
var processAll = Promise.coroutine(function*(){
while(true){
var next = yield db.popNextAsync(); // promisify gives Async suffix
if(!next) return;
out.append(next); // some processing
}
});
在 ES2016 (ES7) 中变成:
var db = Promise.promisifyAll(db); // still need to promisify
async function processAll(){
let next;
while(next = await db.popNextAsync()){
// whatever
out.append(next);
}
}
不过,我认为输出集合也应该是可迭代的(并且是惰性的),所以使用 ES2016 异步迭代器:
var db = Promise.promisifyAll(db);
async function* process(){
while(true){
var val = await db.popNextAsync();
if(!val) return;
// process val;
yield process(val); // yield it forward
}
}
虽然如果我们真的想在这里全力以赴,在将db.popNext
转换为异步迭代器之后,这在 ES2016 async 中变成了符号:
async function* processAll(){
for async(let next of db.asAsyncIterator()){ // need to write this like above
yield process(next); // do some processing
}
}
利用整个 ES2016 异步迭代 API。如果你不能,或者不想使用生成器,你总是可以将 while 循环转换为递归:
function processAll(){ // works on netscape 7
return db.popNextAsync().then(function next(value){
if(!value) return;
out.push(process(value));
return db.popNextAsync().then(next); // after bluebird promisify
});
}
我正在 node.js 中编写一个实用程序,它必须每晚处理和连接大量文件。在同步伪代码中,它看起来像这样(为清楚起见省略了 try / catch):
while (true) {
var next = db.popNext();
if (!next) return;
out.append(next);
}
然而,在我使用的库中 popNext()
实际上是一个节点风格的异步方法,看起来像这样:popNext(callback)
.
由于我是从头开始编写中间件,所以我可以使用 --harmony
(例如,generators), async or bluebird。
理想情况下,我更喜欢这样的东西:
forEachOrdered(db.popNext, (error, next, ok, fail) => {
if(error) return; // skip
// If there was an internal error, terminate the whole loop.
if(out.append(next)) ok();
else fail();
}).then(() => {
// All went fine.
}).catch(e => {
// Fail was called.
});
但是,我对其他 'standard' 解决方案持开放态度。我想知道这个问题最简洁的解决方案是什么?
Edit 仅同时生成所有(在常规 for 循环中)可能无法解决我的问题,因为我们谈论的是 100k,对于我必须的每个项目打开并读取一个文件,所以我可能 运行 没有文件描述符。
这是一个使用 bluebird 协程的解决方案,使用您的 "ideal" 代码:
var db = Promise.promisifyAll(db);
var processAll = Promise.coroutine(function*(){
while(true){
var next = yield db.popNextAsync(); // promisify gives Async suffix
if(!next) return;
out.append(next); // some processing
}
});
在 ES2016 (ES7) 中变成:
var db = Promise.promisifyAll(db); // still need to promisify
async function processAll(){
let next;
while(next = await db.popNextAsync()){
// whatever
out.append(next);
}
}
不过,我认为输出集合也应该是可迭代的(并且是惰性的),所以使用 ES2016 异步迭代器:
var db = Promise.promisifyAll(db);
async function* process(){
while(true){
var val = await db.popNextAsync();
if(!val) return;
// process val;
yield process(val); // yield it forward
}
}
虽然如果我们真的想在这里全力以赴,在将db.popNext
转换为异步迭代器之后,这在 ES2016 async 中变成了符号:
async function* processAll(){
for async(let next of db.asAsyncIterator()){ // need to write this like above
yield process(next); // do some processing
}
}
利用整个 ES2016 异步迭代 API。如果你不能,或者不想使用生成器,你总是可以将 while 循环转换为递归:
function processAll(){ // works on netscape 7
return db.popNextAsync().then(function next(value){
if(!value) return;
out.push(process(value));
return db.popNextAsync().then(next); // after bluebird promisify
});
}