Imagine the following Promise-based example:
function treadLightly() {
return Promise.resolve()
.then(function() { allocateResource(); })
.then(function() { doRiskyOperation(); })
.then(function() { releaseResource(); })
.catch(function() { releaseResource(); })
;
}
We want to call releaseResource()
whether or not doRiskyOperation()
resolves or rejects. But there's a whiff of code smell in calling releaseResource()
in two separate places.
What we really want is something equivalent to javascript's finally
.
Is there a cleaner way to code this in Promises?
ES2015 (ES6) promises don't have finally
yet. There's a Stage 2 proposal for it, which means there's basically no chance of it being in ES2017 but some possibility of it being in ES2018. I haven't seen a really good pattern (as opposed to an actual finally
feature) for it.
Some third-party promise libraries have it, including Bluebird, Q, and when. jQuery's Deferred's promises also have it, in the form of always
.
The proposal has a polyfill you can use:
if (typeof Promise !== 'function') {
throw new TypeError('A global Promise is required');
}
if (typeof Promise.prototype.finally !== 'function') {
var speciesConstructor = function (O, defaultConstructor) {
var C = typeof O.constructor === 'undefined' ? defaultConstructor : O.constructor;
var S = C[Symbol.species];
return S == null ? defaultConstructor : S;
var C = O.constructor;
if (typeof C === 'undefined') {
return defaultConstructor;
}
if (!C || (typeof C !== 'object' && typeof C !== 'function')) {
throw new TypeError('O.constructor is not an Object');
}
var S = C[Symbol.species];
if (S == null) {
return defaultConstructor;
}
if (typeof S === 'function' && S.prototype) {
return S;
}
throw new TypeError('no constructor found');
};
var shim = {
finally(onFinally) {
var handler = typeof onFinally === 'function' ? onFinally : () => {};
var C;
var newPromise = Promise.prototype.then.call(
this, // throw if IsPromise(this) is not true
x => new C(resolve => resolve(handler())).then(() => x),
e => new C(resolve => resolve(handler())).then(() => { throw e; })
);
C = speciesConstructor(this, Promise); // throws if SpeciesConstructor throws
return newPromise;
}
};
Promise.prototype.finally = shim.finally;
}
Although @T.J. Crowder's answer is correct, a practical solution to your issue can be adding another .then
at the end:
function treadLightly() {
return Promise.resolve()
.then(function() { allocateResource(); })
.then(function() { doRiskyOperation(); })
.catch(function() { /* Do nothing, or log the error */ })
.then(function() { releaseResource(); })
;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With