迭代node.js中的大量异步调用/结果(使用ES6 / async / bluebird / generators)?

时间:2015-07-12 12:18:20

标签: javascript node.js promise bluebird node-async

我在node.js中编写一个实用程序,它必须每晚处理和连接大量文件。在同步伪代码中它看起来像那样(为了清楚起见省略try / catch):

while (true) {
    var next = db.popNext();
    if (!next) return;

    out.append(next);
}

但是,在库中,我使用popNext()实际上是一种节点式异步方法,而且看起来像这样:popNext(callback)

由于我从头开始编写中间件,我可以使用--harmony(例如generators),asyncbluebird

理想情况下,我更喜欢以下内容:

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.
});

但是,我愿意接受其他标准的'解决方案。我想知道这个问题最简洁的解决办法是什么?

编辑只是同时产生所有(在常规for循环中)可能无法解决我的问题,因为我们正在讨论100k以及我所拥有的每个项目打开并读取文件,所以我可能会用完文件描述符。

1 个答案:

答案 0 :(得分:4)

这是一个使用蓝鸟协同程序的解决方案,使用你的"理想"代码:

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 for notation:

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
   });
}