I am attempting to use JavaScript promises in a project and the order of events are unexpected. I have narrowed it down into a small demo using a test promise.
testPromises = function(promiseNum){
return new Promise(function(resolve, reject) {
console.log ('handling promise '+promiseNum);
setTimeout(function(){
console.log("resolving testPromises "+promiseNum);
resolve();
},2000)
});
};
And then I call it like so:
testPromises(1).then(testPromises(2)).then(testPromises(3))
.then(function(value) {
console.log('all promises resolved');
}, function(reason) {
console.log('some promise was rejected');
});
This is the console output:
handling promise 1
handling promise 2
handling promise 3
resolving testPromises 1
all promises resolved
resolving testPromises 2
resolving testPromises 3
How can I get an output of:
handling promise 1
resolving testPromises 1
handling promise 2
resolving testPromises 2
handling promise 3
resolving testPromises 3
all promises resolved
I am attempting to use JavaScript promises in a project and the order of events are unexpected. I have narrowed it down into a small demo using a test promise.
testPromises = function(promiseNum){
return new Promise(function(resolve, reject) {
console.log ('handling promise '+promiseNum);
setTimeout(function(){
console.log("resolving testPromises "+promiseNum);
resolve();
},2000)
});
};
And then I call it like so:
testPromises(1).then(testPromises(2)).then(testPromises(3))
.then(function(value) {
console.log('all promises resolved');
}, function(reason) {
console.log('some promise was rejected');
});
This is the console output:
handling promise 1
handling promise 2
handling promise 3
resolving testPromises 1
all promises resolved
resolving testPromises 2
resolving testPromises 3
How can I get an output of:
handling promise 1
resolving testPromises 1
handling promise 2
resolving testPromises 2
handling promise 3
resolving testPromises 3
all promises resolved
Share
Improve this question
asked Oct 24, 2016 at 22:00
amaackamaack
961 silver badge8 bronze badges
1
- 1 Like @jacub.g has pointed out all your promises have all been created before the then function is even called. A nice solution for this type of promise looping is to use a helper function. Bluebird has a map function that has a concurrency option, in this case you could set this to 1. – Keith Commented Oct 24, 2016 at 22:21
3 Answers
Reset to default 9.then()
expects a function reference. When you do something like this:
.then(testPromises(2))
you are executing the function testPromise()
immediately and passing the return value to .then()
. This is almost never what you want (unless testPromises()
returned another function) because for .then()
to do its job, you MUST pass it a function reference (a function that it can call sometime later). If you execute the function immediately, then .then()
can't do its job and call it LATER.
Instead, what you want is this:
.then(function() {
return testPromises(2);
})
Or, you could use .bind()
:
.then(testPromises.bind(null, 2))
So, your whole chain would look like this:
testPromises(1).then(function() {
return testPromises(2);
}).then(function() {
return testPromises(3);
}).then(function(value) {
console.log('all promises resolved');
}, function(reason) {
console.log('some promise was rejected');
});
Or, using .bind()
testPromises(1)
.then(testPromises.bind(null, 2))
.then(testPromises.bind(null, 3))
.then(function(value) {
console.log('all promises resolved');
}, function(reason) {
console.log('some promise was rejected');
});
If you're doing this a lot, you could make a curry wrapper for testPromises()
that would return another function. This is essentially what .bind()
above was doing, but the syntax is a little prettier to use once you've declared your wrapper function.
function tp(arg) {
return function() {
return testPromises(arg);
}
}
Then, because that wrapper returns another function, you could then do:
testPromises(1).then(tp(2)).then(tp(3))
.then(function(value) {
console.log('all promises resolved');
}, function(reason) {
console.log('some promise was rejected');
});
This is the code to get the output you want:
testPromises(1).then(function(){
return testPromises(2);
}).then(function(){
return testPromises(3);
}).then(function(value) {
console.log('all promises resolved');
}, function(reason) {
console.log('some promise was rejected');
});
Promises are resolved asynchronously, but created synchronously.
testPromises(1).then(testPromises(2)).then(testPromises(3))
is more or less like doing
p1 = testPromises(1)
p2 = testPromises(2)
p3 = testPromises(3)
p1.then(p2).then(p3)
Since new Promise
is synchronous, the first 3 lines you'll see will be
handling promise 1
handling promise 2
handling promise 3
Well,
handling promise 1
handling promise 2
handling promise 3
occurs first because these calls are independent from the Promise workflow, they are executed right when you create each Promise
object, because to do that you explicit execute each function like testPromises(1)
and so on.
But the problem is that your call to console.log('all promises resolved');
depends only on testPromises(1)
being resolved, all your other promises are detached.
In order do sync them, each .then
call have to return a Promise, and you can do it by using a function
and returning the Promises testPromises
function already returns.
testPromises = function(promiseNum){
return new Promise(function(resolve, reject) {
console.log ('handling promise '+promiseNum);
setTimeout(function(){
console.log("resolving testPromises "+promiseNum);
resolve();
},0)
});
};
testPromises(1).then(function() {
return testPromises(2);
}).then(function() {
return testPromises(3);
}).then(function(value) {
console.log('all promises resolved');
}, function(reason) {
console.log('some promise was rejected');
});