如何按顺序执行Promise数组?

IT技术 javascript ember.js promise rsvp.js
2021-01-12 05:56:28

我有一系列需要按顺序运行的Promise。

var promises = [promise1, promise2, ..., promiseN];

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

RSVP.all(promises).then(...); 

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

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

RSVP.resolve()
    .then(promise1)
    .then(promise2)
    ...
    .then(promiseN)
    .then(...);

但问题是Promise的数量各不相同,而且Promise的数组是动态构建的。

6个回答

如果您已经将它们放在数组中,那么它们已经在执行。如果你有一个Promise,那么它已经在执行。这不是Promise的问题(Task即,在.Start()方法方面,它们不像 C# s )。.all不执行任何事情,它只返回一个Promise。

如果您有一组 promise 返回函数:

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

tasks.reduce(function(cur, next) {
    return cur.then(next);
}, RSVP.resolve()).then(function() {
    //all executed
});

或值:

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
});
谢谢您的回答。您是正确的,创建一个Promise已经意味着它正在执行,所以我的问题没有正确形成。我最终在没有Promise的情况下以不同的方式解决了我的问题。
2021-03-14 05:56:28
如果您已经将它们放在一个数组中,那么它们已经在执行。- 这句话应该是粗体+更大的字体。理解这一点很重要。
2021-03-17 05:56:28
这是构建不需要参数的同构Promise树的极好方法。它完全等同于使用 next_promise 指针自己构建树,如果Promise集在参数等方面不是同构的,您需要这样做。这只是 reduce 函数正在执行指向当前的指针-叶位给你。如果您的某些事情可以同时发生,您还需要构建自己的树。在Promise树中,分支是序列,叶子是并发的。
2021-03-20 05:56:28
如果这些Promise中的任何一个失败,错误将永远不会被拒绝并且Promise永远不会解决......
2021-04-08 05:56:28
@SSHThis 首先,哇。其次,之前的响应被传递给.then,在这个例子中它只是被忽略了......
2021-04-09 05:56:28

使用 ECMAScript 2017 异步函数,它会像这样完成:

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

    for (const fn of tasks) {
        await fn();
    }
}

你现在可以使用BabelJS来使用异步函数了

这应该是现在(2020 年)的默认方法。对于第一次使用的用户来说,注意两件事可能很重要: 1. 一旦Promise存在,它就已经开始了。所以 2.fn1, fn2, fn3这里是函数非常重要,例如() => yourFunctionReturningAPromise(),而不是仅仅yourFunctionReturningAPromise(). 这也是为什么await fn()要反才的原因await fn在官方文档中查看更多信息很抱歉作为评论发布,但编辑队列已满:)
2021-03-15 05:56:28

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>

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

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

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

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

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

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

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

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

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

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

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

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

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

这好多了,但是我觉得你仍然偏离主题。这在许多关于 Promise 的答案中很常见,人们似乎并没有花时间阅读问题,而是简单地评论他们个人理解的 Promise 的某些方面。原始问题不涉及并行执行,甚至一点也不涉及,并且它确实清楚地表明需要简单地链接 via then,您已经提供了很多额外的信息,这些信息隐藏了所提出问题的答案。
2021-03-20 05:56:28
@DavidMcMullin“....它确实清楚地表明只需要通过 then 进行链接...”但实际上他指出Promise序列是动态建立的。所以他确实需要了解如何构造一棵树,即使在这种情况下它是树“线性序列”的简单子集。您仍然必须通过维护对链中最后一个Promise的引用并向其添加新Promise来构建它。
2021-03-28 05:56:28
当 OP 说“Promise的数量不同并且Promise的数组是动态构建的”时,我很确定他/他的意思是数组的大小不是预先确定的,因此他/他不能使用简单的Promise.resolve().then(...).then(...)...,并不是说在Promise 执行数组在增长当然,现在一切都没有意义了。
2021-04-07 05:56:28

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

Promise.prototype.sequence = async (promiseFns) => {
  for (let promiseFn of promiseFns) {
    await promiseFn();
  }
}

然后你可以在任何地方使用它,就像 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

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