admin管理员组文章数量:1134247
I have an array of promises that need to run in sequential order.
var promises = [promise1, promise2, ..., promiseN];
Calling RSVP.all will execute them in parallel:
RSVP.all(promises).then(...);
But, how can I run them in sequence?
I can manually stack them like this
RSVP.resolve()
.then(promise1)
.then(promise2)
...
.then(promiseN)
.then(...);
but the problem is that the number of promises varies and array of promises is built dynamically.
I have an array of promises that need to run in sequential order.
var promises = [promise1, promise2, ..., promiseN];
Calling RSVP.all will execute them in parallel:
RSVP.all(promises).then(...);
But, how can I run them in sequence?
I can manually stack them like this
RSVP.resolve()
.then(promise1)
.then(promise2)
...
.then(promiseN)
.then(...);
but the problem is that the number of promises varies and array of promises is built dynamically.
Share Improve this question edited Feb 27, 2017 at 15:04 jakub.g 41.1k12 gold badges100 silver badges139 bronze badges asked Nov 20, 2013 at 15:38 jaaksarvjaaksarv 1,4801 gold badge11 silver badges16 bronze badges 2- from the other answers and downvotes on mine, it seems more people need to read the rsvp README where it explains "The really awesome part comes when you return a promise from the first handler". If you aren't doing this you are really missing out on the expressive power of promises. – Michael Johnston Commented Nov 20, 2013 at 20:06
- Similar question but not framework-specific: stackoverflow.com/q/24586110/245966 – jakub.g Commented Feb 27, 2017 at 15:04
9 Answers
Reset to default 146If you already have them in an array then they are already executing. If you have a promise then it's already executing. This is not a concern of promises (I.E they are not like C# Task
s in that regard with .Start()
method). .all
doesn't execute anything
it just returns a promise.
If you have an array of promise returning functions:
var tasks = [fn1, fn2, fn3...];
tasks.reduce(function(cur, next) {
return cur.then(next);
}, RSVP.resolve()).then(function() {
//all executed
});
Or values:
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
});
With ECMAScript 2017 async functions it would be done like this:
async function executeSequentially() {
const tasks = [fn1, fn2, fn3]
for (const fn of tasks) {
await fn();
}
}
You can use BabelJS to use async functions now
ES7 way in 2017.
<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>
This will execute the given functions sequentially(one by one), not in parallel. The parameter promises
is an array of functions, which return Promise
.
Plunker example with the above code: http://plnkr.co/edit/UP0rhD?p=preview
Yet another approach is to define a global sequence function on the Promise
prototype.
Promise.prototype.sequence = async (promiseFns) => {
for (let promiseFn of promiseFns) {
await promiseFn();
}
}
Then you can use it anywhere, just like Promise.all()
Example
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
Disclaimer: Be careful editing Prototypes!
A second attempt at an answer in which I try to be more explanatory:
First, some requisite background, from the RSVP README:
The really awesome part comes when you return a promise from the first handler...This allows you to flatten out nested callbacks, and is the main feature of promises that prevents "rightward drift" in programs with a lot of asynchronous code.
This is precisely how you make promises sequential, by returning the later promise from the then
of the promise that should finish before it.
It is helpful to think of such a set of promises as a tree, where the branches represent sequential processes, and the leaves represent concurrent processes.
The process of building up such a tree of promises is analogous to the very common task of building other sorts of trees: maintain a pointer or reference to where in the tree you are currently adding branches, and iteratively add things.
As @Esailija pointed out in his answer, if you have an array of promise-returning functions that don't take arguments you can use reduce
to neatly build the tree for you. If you've ever implemented reduce for yourself, you will understand that what reduce is doing behind the scenes in @Esailija's answer is maintaining a reference to the current promise (cur
) and having each promise return the next promise in its then
.
If you DON'T have a nice array of homogeneous (with respect to the arguments they take/return) promise returning functions, or if you need a more complicated structure than a simple linear sequence, you can construct the tree of promises yourself by maintaining a reference to the position in the promise tree where you want to add new promises:
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();
You can build combinations of concurrent and sequential processes by using RSVP.all to add multiple "leaves" to a promise "branch". My downvoted-for-being-too-complicated answer shows an example of that.
You can also use Ember.run.scheduleOnce('afterRender') to ensure that something done in one promise gets rendered before the next promise is fired -- my downvoted-for-being-too-complicated answer also shows an example of that.
All is needed to solve that is a for
loop :)
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);
});
}
I had similar problem, and I made a recursive function which runs functions one by one sequentially.
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();
};
In case you need to collect output from these functions:
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([]);
};
The thing I was after was essentially mapSeries, and I happen to be mapping save over a set of values, and I want the results.
So, here's as far as I got, to help others searching for similar things in the future..
(Note that the context is an Ember app).
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();
}
}
}
then
const seq = new PromiseSequence();
seq.push(() => Promise.resolve(1));
seq.push(() => Promise.resolve(2));
seq.run();
it is also possible to store what promises return in another private var and pass it to callbacks
本文标签: javascriptHow can I execute array of promises in sequential orderStack Overflow
版权声明:本文标题:javascript - How can I execute array of promises in sequential order? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736832182a1954746.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论