【问题标题】:ES6 Promises - something like async.each?ES6 Promises - 类似 async.each 的东西?
【发布时间】:2015-11-08 19:13:43
【问题描述】:

试图弄清楚如何找到功能与 async.eachSeries 完全相同的东西,我需要一个按顺序运行(而不是并行)的异步操作列表,但在本机 ES6 中找不到方法,可以有人建议吗?

附言考虑过生成器/产量,但还没有经验,所以我没有意识到它到底能对我有多大帮助。

编辑 1

每个请求,这里是一个例子:

假设这段代码:

let model1 = new MongooseModel({prop1: "a", prop2: "b"});
let model2 = new MongooseModel({prop1: "c", prop2: "d"});

let arr = [model1 , model2];

现在,我想以一系列而不是并行的方式运行它,因此使用“异步”NPM 很容易:

async.eachSeries(arr, (model, next)=>{
    model.save.then(next).catch(next);
}, err=>{
    if(err) return reject(error);
    resolve();
})

我的问题是:使用 ES6,我可以在本地实现吗?没有 NPM 'async' 包?

编辑 2

使用 async/await 可以轻松完成:

let model1 = new MongooseModel({prop1: "a", prop2: "b"});
let model2 = new MongooseModel({prop1: "c", prop2: "d"});    

let arr = [model1 , model2];

for(let model of arr){
    await model.save();
}

【问题讨论】:

  • 你的意思是,第二个函数取决于第一个函数的结果?
  • 不是必须依赖它,而是必须在前一个完成后运行。
  • 您应该举一个例子并说明示例输入和预期输出,以正确解释问题。
  • 加了个例子,谢谢!

标签: javascript asynchronous ecmascript-6 es6-promise


【解决方案1】:

您可以通过在then 回调中返回来进行链接。例如:

new Promise(function(resolve, reject){ 
  resolve(1)
}).then(function(v){
  console.log(v);
  return v + 1;
}).then(function(v){
  console.log(v)
});

将打印:

1
2

这在异步解析 Promise 时当然有效:

new Promise(function(resolve, reject){
  setTimeout(function(){
    resolve(1);
  }, 1000)
}).then(function(result){
   return new Promise(function(resolve, reject){
     setTimeout(function(){
       console.log(result);
       resolve(result + 1);
     }, 1000)
   });
}).then(function(results){
  console.log(results);
});

印刷:

1
2

【讨论】:

  • 谢谢,但我需要它来动态收集数据,而不是一些已知的静态迭代量..
  • @ShlomiSasson 这只是一个例子。 then 所做的只是返回一个承诺,你传递回调并根据需要创建承诺。
  • Kit,所以你说的是创建一个递归函数,将越来越多的回调附加到“then”方法?
  • @ShlomiSasson 好吧,您不必递归,就像 jfriend00 对 reduce 所做的那样。顺便说一句,有一个 ES7 提议让这个特殊情况更容易:github.com/lukehoban/ecmascript-asyncawait
【解决方案2】:

假设您想对数据数组调用某个异步函数,并且希望它们按顺序调用,而不是并行调用。

async.eachSeries()的界面是这样的:

eachSeries(arr, iterator, [callback])

下面是如何用 Promise 模拟它:

// define helper function that works kind of like async.eachSeries
function eachSeries(arr, iteratorFn) {
    return arr.reduce(function(p, item) {
        return p.then(function() {
            return iteratorFn(item);
        });
    }, Promise.resolve());
}

这假定iteratorFn 将要处理的项目作为参数并返回一个承诺。

这是一个用法示例(假设您有一个已承诺的 fs.readFileAsync())并有一个名为 speak() 的函数,该函数在完成时返回一个承诺:

 var files = ["hello.dat", "goodbye.dat", "genericgreeting.dat"];
 eachSeries(files, function(file) {
     return fs.readFileAsync(file).then(function(data) {
         return speak(data);
     });
 });

这让 Promise 基础架构可以为您排序所有内容。


您也可以手动排序(虽然我不确定为什么):

function eachSeries(arr, iteratorFn) {
    return new Promise(resolve, reject) {
        var index = 0;

        function next() {
            if (index < arr.length) {
                try {
                    iteratorFn(arr[index++]).then(next, reject);
                } catch(e) {
                    reject(e);
                }
            } else {
                resolve();
            }
        }
        // kick off first iteration
        next();
    });
}

或者,手动将 Promise 链接在一起的更简单的版本:

function eachSeries(arr, iteratorFn) {
    var index = 0;

    function next() {
        if (index < arr.length) {
            return iteratorFn(arr[index++]).then(next);
        }
    }
    return Promise.resolve().then(next);
}

请注意其中一个手动版本必须如何将iteratorFn()try/catch 括起来,以确保它是抛出安全的(将异常转换为拒绝)。 .then() 是自动抛出安全的,因此其他方案不必手动捕获异常,因为 .then() 已经为您捕获了它们。

【讨论】:

  • @Startec - .reduce() 接受两个参数。第一个是一个函数,第二个是你要减少的初始值。 Promise.resolve() 正在生成该初始值。然后使用两个参数调用您传递的回调。第一个参数是归约中的当前值。我在我的代码中将其命名为p,它的初始值将是Promise.resolve()。后续值将是我的回调 return p.then(...) 的返回值,这是另一个承诺。回调的第二个参数是我们要减少的数组中的下一个值。
  • @Startec - 有关.reduce() 的文档,请参阅here on MDN。你这样称呼它:var reducedValue = array.reduce(fn, initialValue)。我的Promise.resolve()initialValue。这只是一种通过将 Promise 链初始化为已解决的 Promise 来启动 Promise 链的方法。有点像Promise.resolve().then(fn1).then(fn2).then(fn3)
  • @Startec - 当您考虑到正确的错误处理和潜在的嵌套操作时,promise 无疑是管理事物的最佳方式。此外,它们是 ES7 中 asyncawait 的核心,因此它们也是语言的未来。如果我想对数组进行异步排序,我会在 Bluebird Promise 库中使用 Promise.mapSeries() 之类的东西,这是我通常用来添加扩展 Promise 功能的库。
  • @Startec - 也许您应该发布一个问题,并详细说明您正在尝试做什么,并询问使用 Promises 的最佳方法是什么。我无法从您使用的单词中看出您的实际问题,因此最好查看您的代码,然后人们可以提供更好/不同的方法来做到这一点。我从未发现使用 Promise 比不使用 Promise 需要更多代码的情况。与许多事情一样,编写 Promise 代码有好方法,但不是那么好。
  • @DaveCausey - 是的,第一种方法提前创建了整个迭代长度的初始承诺链,然后让它运行。其他方法一次链接一个新的 Promise,因为前一个 Promise 解决了,旧的 Promise 可以被 GCed。如果您的迭代时间非常长(例如数百万次),那么后一种选项可能会使用明显较少的峰值内存量。
【解决方案3】:

对于喜欢简短回答的人:

[func1, func2].reduce((p, f) => p.then(f), Promise.resolve());

【讨论】:

  • 第二个参数是减少的initial value。例如。这个特定的例子等价于Promise.resolve().then(func1).then(func2)
  • 谢谢,当最后一个promise解决时,你会把代码放在哪里?
  • @SSHThis 将.then(resultFromFunc2 =&gt; console.log("all done")) 放在最后,; 之前。
  • 很有趣,虽然我不确定这与Promise.all([func1, func2]); 有何不同。
  • @ChadJohnson 异步函数按顺序运行,而不是并行运行。
【解决方案4】:

//为运行较低nodejs版本(Azure :/)的系统上传这个 不是最短但我能想到的最好的

例如,让我们说“functionWithPromise”返回一些承诺并期望一些项目。

functionWithPromise(item);

promisesArray =[];

//syncornized
itemsArray.forEach(function (item){
   promisesArray.push(functionWithPromise(item));
});

Promise.all(promisesArray).then(function (values){
//profit
});

【讨论】:

  • 这会并行运行 Promise,而不是按顺序运行
【解决方案5】:

作为answer provided by @jib...的扩展,您还可以将一组项目映射到异步函数,如下所示:

[item1, item2]
    .map(item => async (prev_result) => await something_async(item))
    .reduce((p, f) => p.then(f), Promise.resolve())
    .then(() => console.log('all done'));

注意prev_result 将如何成为先前对something_async 的评估返回的值,这大致相当于async.eachSeriesasync.waterfall 之间的混合。

【讨论】:

    猜你喜欢
    • 2015-11-26
    • 1970-01-01
    • 2011-06-16
    • 2020-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-07
    • 1970-01-01
    相关资源
    最近更新 更多