简体   繁体   中英

How to execute code at the very end of a promise chain in q

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? eg 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.

No, there is none.

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.

So how do I deal with it?

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.

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM