Es6 Promise Settled Callback?
Solution 1:
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) {
constres = () => thisconstfin = () => Promise.resolve(cb()).then(res)
returnthis.then(fin, fin);
};
or more extensively, with passing resolution information to the callback:
Promise.prototype.finally = function(cb) {
constres = () => thisreturnthis.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.
Solution 2:
With async/await, you can a combination of await
with try/finally
, like so:
asyncfunction(somePromise) {
try {
awaitsomePromise();
} 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 blockexportfunctiontransaction(func) {
return db.sequelize.transaction().then(async t => {
Sequelize.cls.set('transaction', t);
try {
awaitfunc();
} 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 await
ing 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.)
Solution 3:
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 stuffif (data.err) {
throw data.err;
}
return data.res;
}).catch(function(err) {
// handle error
});
Solution 4:
Here is my implementation of .finally().
Promise.prototype.finally = function(cb) {
returnthis.then(v=>Promise.resolve(cb(v)),
v=>Promise.reject(cb(v)));
};
I tested it:
(newPromise((resolve,reject)=>{resolve(5);})).finally(x=>console.log(x)); //5
(newPromise((resolve,reject)=>{reject(6);})).finally(x=>console.log(x)); //6
(newPromise((resolve,reject)=>{reject(7);}))
.then(x=>x,y=>y)
.catch(x=>{throw"error";})
.finally(x=>{console.log(x); throw"error"; return x;}) // 7
.then(x=>console.log(x),y=>console.log('e')); //e// Uncaught (in promise) undefined
Solution 5:
Summary:
We now also have access to Promise.prototype.finally()
. This is a function which can be put on the promise chain as a last element to perform some cleanup. it works the following way when compared to Promise.then
and Promise.catch
:
Promise.then
only gets called when the promise is resolved (if you only put it the first argument callback function)Promise.catch
only gets called when the promise is rejectedPromise.finally
always gets called when a promise is fullfilled, so both when the promise gets rejected or resolved.
Example:
letProm = newPromise((res, rej) => {
let random = Math.random();
if (random > 0.5) {
res(1);
} else {
rej('Error occured')
}
});
Prom.then((val) => {
console.log(val);
return val * 10;
}).catch((err) => {
console.log(err);
}).finally(() => {
console.log('finally executed');
})
In the above exapmle we can observe that finally
is always executed regardless if the promise resolves or rejects. Not that finally
should ideally always be at the end of the promise chain to do some cleanup which should be executed regardless of Promise outcome.
The advantage of using finally
is that it avoids the need for code duplication because it is executed for both a resolved and a rejected promise. Otherwise we would have to use hacks like:
.then(onfullfilled, onfullfilled)
or
.then(onfullfilled)
.catch(onfullfilled)
Note that now we have to define the onfullfilled
function as a named function outside the promisehandler itself (or pass in 2 anonymous function copies which is even less elegant). Promise.finally
solves this problem for us.
Post a Comment for "Es6 Promise Settled Callback?"