如何按顺序执行promises数组?

时间:2013-11-20 15:38:45

标签: javascript ember.js promise rsvp.js

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

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

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

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

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

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

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

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

9 个答案:

答案 0 :(得分:123)

如果你已经将它们放在一个数组中,那么它们就已经在执行了。如果你有一个承诺,那么它已经在执行。这不是承诺的关注(I.E在这方面它们不像C#Task那样.Start()方法。 .all不会执行任何操作 它只是回报了一个承诺。

如果你有一系列承诺返回函数:

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

答案 1 :(得分:15)

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

fill

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

答案 2 :(得分:5)

2017年的ES7方式。

tables.field

这将按顺序(逐个)执行给定的功能,而不是并行执行。参数 <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

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

答案 3 :(得分:4)

第二次尝试回答我试图更加解释:

首先,来自RSVP README

的一些必要背景
  

当您从第一个处理程序返回一个promise时,真正令人敬畏的部分就出现了......这可以让您平坦化嵌套的回调,并且是Promise的主要特性,它可以阻止具有大量异步代码的程序中的“向右漂移”

这正是你通过从应该在它之前完成的承诺的then返回后来的承诺来顺序完成承诺的方式。

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

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

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

如果你没有一个很好的齐次数组(关于他们采用/返回的参数)保证返回函数,或者如果你需要一个比简单线性序列更复杂的结构,你可以构造一个承诺树通过维护对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“分支”,从而构建并发和顺序进程的组合。我的down for太复杂的答案就是一个例子。

您还可以使用Ember.run.scheduleOnce('afterRender')来确保在下一个承诺被触发之前呈现在一个承诺中完成的事情 - 我的downvoted-for-too-too-complex答案也显示了一个示例那个。

答案 4 :(得分:0)

我将在这里留下这个答案,因为当我来这里寻找解决问题的方法时,这对我有所帮助。

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

所以,据我所知,FWIW,以帮助其他人在将来搜索类似的东西......

(请注意,上下文是一个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);
    }
});

答案 5 :(得分:0)

我有类似的问题,我做了一个递归函数,它按顺序逐个运行函数。

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([]);
};

答案 6 :(得分:0)

需要一切来解决这是一个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);
  });
}

答案 7 :(得分:0)

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

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

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

示例

const timeout = async ms => {
  return 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

免责声明:请谨慎编辑原型!

答案 8 :(得分:0)

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

还可以将promise返回的内容存储在另一个私有var中,并将其传递给回调函数