如何按顺序执行承诺数组

How can I execute array of promises in sequential order?

本文关键字:承诺 数组 执行 顺序 何按      更新时间:2023-09-26

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

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

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

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

但是,如何按顺序运行它们?

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

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

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

如果你已经在数组中拥有它们,那么它们已经在执行了。如果你有一个承诺,那么它已经在执行了。这不是承诺的问题(即它们在这方面不像 C# Task .Start()方法(。 .all不执行任何内容它只是返回一个承诺。

如果你有一系列 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
});

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

async function executeSequentially() {
    const tasks = [fn1, fn2, fn3]
    for (const fn of tasks) {
        await fn();
    }
}

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

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

另一种方法是在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

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

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

首先,一些必要的背景,来自RSVP自述文件:

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

这正是你使承诺按顺序排列的方式,通过从应该在它之前完成的承诺的then返回后面的承诺。

将这样一组承诺视为一棵树是有帮助的,其中分支表示顺序进程,叶子表示并发进程。

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

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

如果你没有

一个很好的齐次数组(关于它们接受/返回的参数(promise返回函数,或者如果你需要一个比简单线性序列更复杂的结构,你可以通过维护对promise树中你想要添加新promises的位置的引用来自己构造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 构建并发和顺序进程的组合,以将多个"叶子"添加到承诺"分支"。我被否决的"过于复杂的答案"就是一个例子。

你也可以使用 Ember.run.scheduleOnce('afterRender'( 来确保在一个承诺中完成的事情在下一个承诺被触发之前被渲染——我被否决的太复杂答案也展示了一个例子。

解决这一问题只需要一个for循环:)

var promises = [a,b,c];
var chain;
for(let i in promises){
  if(chain) chain = chain.then(promises[i]);
  if(!chain) chain = promises[i]();
}
function a(){
  return new Promise((resolve)=>{
    setTimeout(function(){
      console.log('resolve A');
      resolve();
    },1000);
  });
}
function b(){
  return new Promise((resolve)=>{
    setTimeout(function(){
      console.log('resolve B');
      resolve();
    },500);
  });
}
function c(){
  return new Promise((resolve)=>{
    setTimeout(function(){
      console.log('resolve C');
      resolve();
    },100);
  });
}

我遇到了类似的问题,我做了一个递归函数,它按顺序一个接一个地运行函数。

var tasks = [fn1, fn2, fn3];
var executeSequentially = function(tasks) {
  if (tasks && tasks.length > 0) {
    var task = tasks.shift();
    return task().then(function() {
      return executeSequentially(tasks);
    });
  }
  return Promise.resolve();  
};

如果您需要从这些函数收集输出:

var tasks = [fn1, fn2, fn3];
var executeSequentially = function(tasks) {
  if (tasks && tasks.length > 0) {
    var task = tasks.shift();
    return task().then(function(output) {
      return executeSequentially(tasks).then(function(outputs) {
        outputs.push(output);
        return Promise.resolve(outputs);  
      });
    });
  }
  return Promise.resolve([]);
};

我追求的东西本质上是mapSeries,我碰巧在一组值上映射保存,我想要结果。

所以,据我所知,这是为了帮助其他人在未来搜索类似的东西。

(请注意,上下文是 Ember 应用程序(。

App = Ember.Application.create();
App.Router.map(function () {
    // put your routes here
});
App.IndexRoute = Ember.Route.extend({
    model: function () {
            var block1 = Em.Object.create({save: function() {
                return Em.RSVP.resolve("hello");
            }});
    var block2 = Em.Object.create({save: function() {
            return Em.RSVP.resolve("this");
        }});
    var block3 = Em.Object.create({save: function() {
        return Em.RSVP.resolve("is in sequence");
    }});
    var values = [block1, block2, block3];
    // want to sequentially iterate over each, use reduce, build an array of results similarly to map...
    var x = values.reduce(function(memo, current) {
        var last;
        if(memo.length < 1) {
            last = current.save();
        } else {
            last = memo[memo.length - 1];
        }
        return memo.concat(last.then(function(results) {
            return current.save();
        }));
    }, []);
    return Ember.RSVP.all(x);
    }
});
export type PromiseFn = () => Promise<any>;
export class PromiseSequence {
  private fns: PromiseFn[] = [];
  push(fn: PromiseFn) {
    this.fns.push(fn)
  }
  async run() {
    for (const fn of this.fns) {
      await fn();
    }
  }
}

然后

const seq = new PromiseSequence();
seq.push(() => Promise.resolve(1));
seq.push(() => Promise.resolve(2));
seq.run();

也可以将承诺返回的内容存储在另一个私有 var 中并将其传递给回调