如何按顺序执行承诺数组?

des4xlb0  于 2022-10-20  发布在  其他
关注(0)|答案(9)|浏览(189)

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

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

正在呼叫RSVP。所有将并行执行它们:

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

但是,我怎样才能按顺序运行它们呢?
我可以像这样手动堆叠它们

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

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

stszievb

stszievb1#

如果数组中已经有它们,那么它们已经在执行了。如果你有一个承诺,那么它已经在执行了。这与承诺无关(即,在这方面,它们与C#Task方法不同)。.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
});
1qczuiv0

1qczuiv02#

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

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

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

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

juzqafwq

juzqafwq3#

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的函数数组。
上面代码的柱塞示例:http://plnkr.co/edit/UP0rhD?p=preview

oymdgrw7

oymdgrw74#

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

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
  • 免责声明:小心编辑原型*
pwuypxnk

pwuypxnk5#

第二次尝试回答,我试图更加解释:
首先,来自RSVP README的一些必要背景:
当你从第一个处理程序返回一个promise时,最棒的部分就来了……这允许你平展嵌套的回调,这是promise的主要功能,它可以防止在有大量异步代码的程序中出现“向右漂移”。
这正是通过从应该在其之前完成的promise的then返回后一个promise,从而使promise按顺序进行的方式。
将这样一组承诺看作一棵树是很有帮助的,其中分支表示顺序流程,而叶子表示并发流程。
构建这样一棵承诺树的过程类似于构建其他种类树的非常常见的任务:维护一个指针或引用,指向树中当前添加分支的位置,并迭代添加内容。
正如@Esailija在回答中指出的那样,如果你有一个不带参数的promise返回函数数组,你可以使用reduce为你巧妙地构建树。如果你曾经为自己实现过reduce,你会明白reduce在@Esailija的答案中的幕后工作是维护对当前promise(cur)的引用,并让每个promise在其then中返回下一个promise。
如果你没有一个很好的同构数组(相对于它们获取/返回的参数)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构建并发和顺序流程的组合。所有这些都可以将多个“叶子”添加到一个promise“分支”中。我因回答太复杂而被否决,这就是一个例子。
您也可以使用Ember.run。scheduleOnce('afterRender')以确保在一个承诺中完成的事情在下一个承诺被触发之前得到实现——我因过于复杂而被否决的回答也显示了一个例子。

bogh5gae

bogh5gae6#

只需解决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);
  });
}
a0zr77ik

a0zr77ik7#

我也有类似的问题,我做了一个递归函数,它依次运行函数。

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

r1wp621o8#

我想要的基本上是mapSeries,我碰巧在一组值上Mapsave,我想要结果。
所以,我所能做的就是帮助其他人在未来寻找类似的东西。。
(请注意,上下文是一个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);
    }
});
ff29svar

ff29svar9#

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中并将其传递给回调

相关问题