ES6 Promises - 类似于 async.each?

IT技术 javascript asynchronous ecmascript-6 es6-promise
2021-01-16 16:07:46

试图弄清楚如何找到功能与 async.eachSeries 完全一样的东西,我需要一个按顺序(不是并行)运行的异步操作列表,但找不到在原生 ES6 中执行此操作的方法,任何人都可以提供建议,请?

ps 考虑过发电机/产量,但还没有经验,所以我没有意识到它到底能帮我什么忙。

编辑 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();
}
5个回答

对于那些喜欢简短回答的人:

[func1, func2].reduce((p, f) => p.then(f), Promise.resolve());
第二个参数是减少初始值例如,此特定示例等效于Promise.resolve().then(func1).then(func2)
2021-03-16 16:07:46
谢谢,你会把最后一个Promise解决时执行的代码放在哪里
2021-03-21 16:07:46
@ChadJohnson 异步函数按顺序运行,而不是并行运行。
2021-03-26 16:07:46
@SSHThis 放在.then(resultFromFunc2 => console.log("all done"))最后,在;.
2021-03-27 16:07:46
有趣的是,虽然我不确定这与Promise.all([func1, func2]);.
2021-04-03 16:07:46

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

的界面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将要处理的项目作为参数并返回一个Promise。

这是一个使用示例(假设您有一个 promisified fs.readFileAsync())并调用speak()一个函数该函数在完成后返回一个 promise:

 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 - 当您考虑正确的错误处理和潜在的嵌套操作时,Promise是管理事物的最佳方式。此外,它们是ES7的核心,async也是awaitES7 语言的未来。如果我想对数组进行异步排序,我会使用Promise.mapSeries()Bluebird Promise库中的类似内容,这是我通常用来添加扩展Promise功能的库。
2021-03-17 16:07:46
@Startec - 也许您应该发布一个问题,详细描述您要做什么,并询问如何使用 Promises 做到这一点。我无法仅从您使用的词中看出您的实际问题,因此最好查看您的代码,然后人们可以提供更好/不同的方法来做到这一点。我从未发现使用 Promise 比不使用 Promise 需要更多代码的情况。与许多事情一样,有编写 Promise 代码的好方法,但不是很好的方法。
2021-04-01 16:07:46
@Startec -在 MDN查看这里的文档.reduce()你这样称呼它: var reducedValue = array.reduce(fn, initialValue)我的Promise.resolve()就是那个initialValue这只是通过将其初始化为已解决的Promise来启动Promise链的一种方式。这有点像Promise.resolve().then(fn1).then(fn2).then(fn3)
2021-04-06 16:07:46
@DaveCausey - 是的,第一种方法预先创建了整个迭代长度的初始Promise链,然后让它运行。其他方法一次链接一个新的 promise,因为前一个 promise 已解决,并且旧的 promise 可以被 GCed。如果您有一个非常长的迭代(如数百万),那么后一个选项可能会使用明显较低的峰值内存量。
2021-04-08 16:07:46
@Startec -.reduce()接受两个参数。第一个是函数,第二个是您要减少的初始值。 Promise.resolve()正在产生那个初始值。然后使用两个参数调用您传递的回调。第一个参数是减少中的当前值。p在我的代码中命名了它,它的初始值将是Promise.resolve(). 后续值将是我的回调的返回值,return p.then(...)这是另一个Promise。回调的第二个参数是我们要减少的数组中的下一个值。
2021-04-10 16:07:46

作为@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.eachSeries之间的混合async.waterfall

您可以通过在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

Kit,所以你说的是创建一个递归函数,将越来越多的回调附加到“then”方法上?
2021-03-15 16:07:46
@ShlomiSasson 好吧,您不必递归,就像 jfriend00 对 reduce 所做的那样。顺便说一句,有一个 ES7 提议可以使这个特殊情况更容易:github.com/lukehoban/ecmascript-asyncawait
2021-03-19 16:07:46
@ShlomiSasson 这只是一个例子。所有then做的是返回一个Promise,你通过回调并根据需要创建的Promise。
2021-03-27 16:07:46
谢谢,但我需要它来动态收集数据,而不是一些静态的已知迭代量。
2021-03-28 16:07:46

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

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

functionWithPromise(item);

promisesArray =[];

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

Promise.all(promisesArray).then(function (values){
//profit
});
这并行运行Promise,而不是按顺序运行
2021-04-01 16:07:46