如何按顺序执行promises数组?

jaa*_*arv 76 javascript promise ember.js rsvp.js

我有一系列承诺需要按顺序运行.

var promises = [promise1, promise2, ..., promiseN];
Run Code Online (Sandbox Code Playgroud)

调用RSVP.all将并行执行它们:

RSVP.all(promises).then(...); 
Run Code Online (Sandbox Code Playgroud)

但是,我怎么能按顺序运行它们呢?

我可以像这样手动堆叠它们

RSVP.resolve()
    .then(promise1)
    .then(promise2)
    ...
    .then(promiseN)
    .then(...);
Run Code Online (Sandbox Code Playgroud)

但问题是承诺的数量各不相同,承诺数组是动态建立的.

Esa*_*ija 129

如果你已经将它们放在一个数组中,那么它们就已经在执行了.如果你有一个承诺,那么它已经在执行.这不是承诺的关注点(IE Task在这方面与.Start()方法不同,它们不像C#)..all没有执行任何它只是返回一个承诺.

如果你有一组promise承诺返回函数:

var tasks = [fn1, fn2, fn3...];

tasks.reduce(function(cur, next) {
    return cur.then(next);
}, RSVP.resolve()).then(function() {
    //all executed
});
Run Code Online (Sandbox Code Playgroud)

或价值观:

var idsToDelete = [1,2,3];

idsToDelete.reduce(function(cur, next) {
    return cur.then(function() {
        return http.post("/delete.php?id=" + next);
    });
}, RSVP.resolve()).then(function() {
    //all executed
});
Run Code Online (Sandbox Code Playgroud)

  • _如果你已经将它们放在一个数组中,那么它们已经在执行._ - 这个短语应该是粗体+更大的字体.理解是至关重要的. (5认同)
  • 这是构建不需要参数的同构承诺树的极好方法.它完全等同于使用next_promise指针自己构建树,如果Promise的集合与参数等不相同,则需要这样做.只是reduce函数正在执行指向当前的指针-leaf bit for you.如果您的某些事情可以同时发生,您还需要构建自己的树.在承诺树中,分支是序列,叶子是并发的. (3认同)
  • 如果任何这些承诺失败,错误将永远不会被拒绝,承诺永远不会解决...... (3认同)
  • 使用本机Promise重写了上面的代码http://jsfiddle.net/5wecfv2w/ (2认同)

uje*_*tor 18

使用ECMAScript 2017异步函数,可以这样做:

async function executeSequentially() {
    const tasks = [fn1, fn2, fn3]

    for (const fn of tasks) {
        await fn()
    }
}
Run Code Online (Sandbox Code Playgroud)

您现在可以使用BabelJS来使用异步功能

  • 这应该是现在(2020 年)的默认方法。对于初次使用的用户来说,在这里注意两件事可能很重要: 1. 一旦承诺存在,它就已经开始了。因此,2.“fn1、fn2、fn3”是函数,例如“() => yourFunctionReturningAPromise()”,而不仅仅是“yourFunctionReturningAPromise()”,这一点非常重要。这也是为什么需要“await fn()”而不是“await fn”的原因。查看更多内容[在官方文档中](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises)。抱歉发表评论,但编辑队列已满:) (6认同)

all*_*kim 7

2017年的ES7方式.

  <script>
  var funcs = [
    _ => new Promise(resolve => setTimeout(_ => resolve("1"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("2"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("3"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("4"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("5"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("6"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("7"), 1000))
  ];
  async function runPromisesInSequence(promises) {
    for (let promise of promises) {
      console.log(await promise());
    }
  }
  </script>
  <button onClick="runPromisesInSequence(funcs)">Do the thing</button>
Run Code Online (Sandbox Code Playgroud)

这将按顺序(逐个)执行给定的功能,而不是并行执行.该参数promises是一个返回的函数数组Promise.

使用上述代码的Plunker示例:http://plnkr.co/edit/UP0rhD?p = preview


Mic*_*ton 5

第二次尝试回答,我试图在其中更具解释性:

首先,一些必要的背景,来自RSVP README

当您从第一个处理程序返回承诺时,真正令人敬畏的部分出现了......这允许您扁平化嵌套回调,并且是承诺的主要功能,可以防止在具有大量异步代码的程序中“向右漂移”。

这正是你如何使承诺按顺序进行,通过从then应该在它之前完成的承诺中返回后面的承诺。

把这样的一组 promise 想象成一棵树,其中的分支代表顺序进程,而叶子代表并发进程,这会很有帮助。

构建这样一棵 Promise 树的过程类似于构建其他类型树的非常常见的任务:维护指向当前在树中添加分支的位置的指针或引用,并迭代地添加内容。

正如@Esalija 在他的回答中指出的那样,如果您有一组不带参数的承诺返回函数,您可以使用reduce它们为您整齐地构建树。如果您曾经为自己实现过 reduce,您就会明白在@Esailja 的回答中,reduce 在幕后所做的是维护对当前承诺 ( cur)的引用,并让每个承诺返回其then.

如果你没有一个很好的同构数组(关于它们接受/返回的参数)承诺返回函数,或者如果你需要一个比简单线性序列更复杂的结构,你可以通过维护自己构建承诺树对承诺树中要添加新承诺的位置的引用:

var root_promise = current_promise = Ember.Deferred.create(); 
// you can also just use your first real promise as the root; the advantage of  
// using an empty one is in the case where the process of BUILDING your tree of 
// promises is also asynchronous and you need to make sure it is built first 
// before starting it

current_promise = current_promise.then(function(){
  return // ...something that returns a promise...;
});

current_promise = current_promise.then(function(){
  return // ...something that returns a promise...;
});

// etc.

root_promise.resolve();
Run Code Online (Sandbox Code Playgroud)

您可以通过使用 RSVP.all 将多个“叶子”添加到承诺“分支”来构建并发和顺序进程的组合。我因过于复杂而被低估的答案显示了一个例子。

您还可以使用 Ember.run.scheduleOnce('afterRender') 来确保在一个承诺中完成的某些事情在下一个承诺被触发之前被渲染——我的反对票太复杂的答案也显示了一个例子。

  • 这好多了,但是我觉得你仍然偏离主题。这在许多关于 Promise 的答案中很常见,人们似乎并没有花时间阅读问题,而是简单地评论他们个人理解的 Promise 的某些方面。原始问题不涉及并行执行,甚至不涉及并行执行,并且它清楚地表明只需要通过 `then` 进行链接,你已经提供了很多额外的信息,这些信息隐藏了所问问题的答案. (3认同)

Ben*_*ing 5

另一种方法是在原型上定义全局序列函数Promise

Promise.prototype.sequence = async (promiseFns) => {
  for (let promiseFn of promiseFns) {
    await promiseFn();
  }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以在任何地方使用它,就像 Promise.all()

例子

const timeout = async ms => new Promise(resolve =>
  setTimeout(() => {
    console.log("done", ms);
    resolve();
  }, ms)
);

// Executed one after the other
await Promise.sequence([() => timeout(1000), () => timeout(500)]);
// done: 1000
// done: 500

// Executed in parallel
await Promise.all([timeout(1000), timeout(500)]);
// done: 500
// done: 1000
Run Code Online (Sandbox Code Playgroud)

免责声明:小心编辑原型!