Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ES6 promise settled callback?

I want to run the same action whether my Promise resolved successfully or not. I don't want to bind the same function to both args of .then. Isn't there a .always like jQuery has? If not, how do I achieve this?

like image 511
mpen Avatar asked Sep 02 '15 20:09

mpen


People also ask

What is a settled promise?

allSettled() The Promise. allSettled() method returns a promise that fulfills after all of the given promises have either fulfilled or rejected, with an array of objects that each describes the outcome of each promise.

Does callback return a promise?

The idea is to create a new Promise object that wraps around the callback function. If the callback function returns an error, we reject the Promise with the error. If the callback function returns non-error output, we resolve the Promise with the output.

Can I use promise allSettled?

allSettled() Method. The Promise is a JavaScript object which can be in three states pending, fulfilled or rejected. The Promise. allSettled() method in JavaScript is used to get a promise when all inputs are settled that is either fulfilled or rejected.

Does promise return After resolve?

The Promise. resolve() method "resolves" a given value to a Promise . If the value is a promise, that promise is returned; if the value is a thenable, Promise. resolve() will call the then() method with two callbacks it prepared; otherwise the returned promise will be fulfilled with the value.


3 Answers

Isn't there a .always like jQuery has?

No, there's not (yet). Though there is an active proposal, so maybe ES2018.
Yes, there is: promise .finally() is part of the standard since ES2018.

If not, how do I achieve this?

You can implement the finally method yourself like this:

Promise.prototype.finally = function(cb) {
    const res = () => this
    const fin = () => Promise.resolve(cb()).then(res)
    return this.then(fin, fin);
};

or more extensively, with passing resolution information to the callback:

Promise.prototype.finally = function(cb) {
    const res = () => this
    return this.then(value =>
        Promise.resolve(cb({state:"fulfilled", value})).then(res)
    , reason =>
        Promise.resolve(cb({state:"rejected", reason})).then(res)
    );
};

Both ensure that the original resolution is sustained (when there is no exception in the callback) and that promises are awaited.

like image 109
Bergi Avatar answered Oct 23 '22 21:10

Bergi


With async/await, you can a combination of await with try/finally, like so:

async function(somePromise) {
  try {
    await somePromise();
  } finally {
    // always run this-- even if `somePromise` threw something
  }
}

Here's a real example I have running in production with Node, using Babel's async-to-generator plugin.

// Wrap promisified function in a transaction block
export function transaction(func) {
  return db.sequelize.transaction().then(async t => {
    Sequelize.cls.set('transaction', t);
    try {
      await func();

    } finally {
      await t.rollback();
    }
  });
}

I use this code inside a mocha test alongside the Sequelize ORM to start a DB transaction, and regardless of the outcome of the DB calls within the test, always roll back at the end.

This is roughly analogous to Bluebird's .finally() method, but IMO, far nicer syntax!

(Note: In case you're wondering why I'm not awaiting on the first Promise- it's an implementation detail of Sequelize. It uses CLS to 'bind' a SQL transaction to a Promise chain. Anything that incurs inside the same chain is scoped to the transaction. Anything outside isn't. Therefore, awaiting on the Promise would have 'closed' the transaction block and broken the chain. I threw this example in to show you how 'vanilla' Promise handling can be mixed alongside async functions, and play well together.)

like image 33
Lee Benson Avatar answered Oct 23 '22 22:10

Lee Benson


If you don't/can't update the prototype, the way to hack a finally is:

executeMyPromise()
.then(function(res){ return {res: res}; })
.catch(function(err){ return {err: err}; })
.then(function(data) {
    // do finally stuff
    if (data.err) {
        throw data.err;
    }
    return data.res;
}).catch(function(err) {
    // handle error
});
like image 3
user2426679 Avatar answered Oct 23 '22 21:10

user2426679