Reputation: 3341
Let's say you have:
function setTimeoutPromise(ms) {
var defer = Q.defer();
setTimeout(defer.resolve, ms);
return defer.promise;
}
and then you have something like:
function foo(ms) {
return setTimeoutPromise(ms).then(function () {
console.log('foo');
});
}
function afterItAll() {
console.log('after it all');
}
foo(100).then(function () {
console.log('after foo');
}).then(afterItAll);
Is there a way to modify foo so that afterItAll is executed after the after foo block? e.g. something like:
function foo(ms) {
return setTimeoutPromise(ms).then(function () {
console.log('foo');
}).SOMEPROMISECONSTRUCT(function () {
console.log('after it all');
});
}
foo(100).then(function () {
console.log('after foo');
});
The reason I ask is that I am developing an API where the user will make several of these foo calls and it would greatly cut down on the user's code if the after foo code was automatically executed after these API calls. I know I can use a callback to accomplish this, but I'd really like to stick with just using promises.
Upvotes: 1
Views: 3082
Reputation: 783
OK! I hope my experience helps you.
I had very similar problem.
I wanted mysql connection pool to be released after all sql statement executed, or failed... like below
getConnection
.then(exeuteSQL('select ...'))
.then(exeuteSQL('select ...'))
.then(null, function(err){ //err handling })
....
.. finally execute pool release
This can be done .done() method like this
getConnection
.then(exeuteSQL('select ...'))
.then(exeuteSQL('select ...'))
.then(null, function(err){ //err handling })
....
.done(function(){
console.log('this line always executed!');
req.conn.release(); // assuming connection attached to request object before
})
PS) My application Environment is node.js and using 'q' promise module
Upvotes: 0
Reputation: 664196
No, there is no such promise construct. A promise does not - can not - know whether it is the end of a chain, or whether some other code will attach another link to it.
There is however nothing wrong with combining promises and callback code:
function foo(ms, within) {
return setTimeoutPromise(ms).then(function () {
console.log('foo');
})
.then(within)
.then(function afterFooAll() { // possibly use `finally` here?
console.log('cleanup');
});
}
foo(100, function () {
console.log('inside foo');
}) // now returns a promise for cleanup been done
I'm not sure what your actual use case is here, but you also might want to have a look at Bluebird's Promise.using
resource management pattern.
Upvotes: 2
Reputation: 276276
Well, let's see what you're asking here:
Is there a way to modify foo so that afterItAll is executed after the after foo block?
This is effectively asking:
Is there any way to know when no more
.then
handlers will be added to a specific promise?
Which, given an arbitrary function we can decide to add a fooResult.then(function(){})
as the very last thing in the program before we return
from it, so it's like asking:
Is there any way to know when/if a function will return?
Which, given a whole program as a function, is like asking:
Is there any way to know if a program will ever halt?
It's not an easy thing to do to say the least. Not only is this feature non existent, it is theoretically impossible.
Bergi's answer gives you a pretty good idea. The core here, that we fought for in Bluebird is nesting.
Because we want something that's impossible in the general case we have to invert control, like callbacks do:
function transaction(fn){
// Promise.resolve().then( in ES6/Bluebird promises
return Q().then(function(){
return fn()
}).finally(function(){ // same in Bluebird, in ES6 that's `.then(fn, fn)`
console.log("after it all!");
})
}
This would let you do:
transaction(function(){
return setTimeoutPromise().then(more).then(more);
});
Which would run the setTimeoutPromise and then the more
and then the other more and will log "after it all" after both are done. This pattern is very useful for DB drivers and resource acquisition.
Upvotes: 3