javascript - Promises: Execute something regardless of resolve/reject? -
using promises design pattern, possible implement following:
var a, promise if promise.resolve = promise.responsevalue; if promise.reject = "failed" after resolution/rejection. not async!! send somewhere, not asynchronously. //not promise
what i'm looking finally
in try - catch
situation.
ps: i'm using es6 promise polyfill on nodejs
if return value catch
, can use then
on result of catch
.
thepromise.then(result => dosomething(result) .catch(error => handleerrorandreturnsomething(error)) .then(resultorreturnfromcatch => /* ... */);
...but means you're converting rejection resolution (by returning catch
rather throwing or returning rejected promise), , relies on fact.
if want transparently passes along resolution/rejection without modifying it, there's nothing built es2015 ("es6") promises that, it's easy write (this in es2015, have es5 translation below):
{ let worker = (p, f, done) => { return p.constructor.resolve(f()).then(done, done); }; object.defineproperty(promise.prototype, "finally", { value(f) { return this.then( result => worker(this, f, () => result), error => worker(this, f, () => { throw error; }) ); } }); }
example:
{ let worker = (p, f, done) => { return p.constructor.resolve(f()).then(done, done); }; object.defineproperty(promise.prototype, "finally", { value(f) { return this.then( result => worker(this, f, () => result), error => worker(this, f, () => { throw error; }) ); } }); } test("p1", promise.resolve("good")).finally( () => { test("p2", promise.reject("bad")); } ); function test(name, p) { return p.then( result => { console.log(name, "initial resolution:", result); return result; }, error => { console.log(name, "initial rejection; propagating it"); throw error; } ) .finally(() => { console.log(name, "in finally"); }) .then( result => { console.log(name, "resolved:", result); }, error => { console.log(name, "rejected:", error); } ); }
a couple of notes on that:
note use of
this.constructor
we're callingresolve
on whatever kind of promise (including possible subclass) created original promise; consistent howpromise.resolve
, others work, , important part of supporting subclassed promises.the above intentionally not including argument
finally
callback, , no indication of whether promise resolved or rejected, in order consistentfinally
in classictry-catch-finally
structure. if 1 wanted, 1 pass of information callback.similarly, above not use value returned
finally
callback except if it's promise, waits promise settle before allowing chain continue.
here's es5 translation of that:
(function() { function worker(ctor, f, done) { return ctor.resolve(f()).then(done, done); } object.defineproperty(promise.prototype, "finally", { value: function(f) { var ctor = this.constructor; return this.then( function(result) { return worker(ctor, f, function() { return result; }); }, function(error) { return worker(ctor, f, function() { throw error; }); } ); } }); })();
example:
(function() { function worker(ctor, f, done) { return ctor.resolve(f()).then(done, done); } object.defineproperty(promise.prototype, "finally", { value: function(f) { var ctor = this.constructor; return this.then( function(result) { return worker(ctor, f, function() { return result; }); }, function(error) { return worker(ctor, f, function() { throw error; }); } ); } }); })(); test("p1", promise.resolve("good")).finally(function() { test("p2", promise.reject("bad")); }); function test(name, p) { return p.then( function(result) { console.log(name, "initial resolution:", result); return result; }, function(error) { console.log(name, "initial rejection; propagating it"); throw error; } ) .finally(function() { console.log(name, "in finally"); }) .then( function(result) { console.log(name, "resolved:", result); }, function(error) { console.log(name, "rejected:", error); } ); }
i think simplest way integrate functionality promise polyfill in es5.
or if prefer subclass promise
rather modifying prototype:
let promisex = (() => { let worker = (p, f, done) => { return p.constructor.resolve(f()).then(done, done); }; class promisex extends promise { finally(f) { return this.then( result => worker(this, f, () => result), error => worker(this, f, () => { throw error; }) ); } } promisex.resolve = promise.resolve; promisex.reject = promise.reject; return promisex; })();
example:
let promisex = (() => { let worker = (p, f, done) => { return p.constructor.resolve(f()).then(done, done); }; class promisex extends promise { finally(f) { return this.then( result => worker(this, f, () => result), error => worker(this, f, () => { throw error; }) ); } } promisex.resolve = promise.resolve; promisex.reject = promise.reject; return promisex; })(); test("p1", promisex.resolve("good")).finally( () => { test("p2", promisex.reject("bad")); } ); function test(name, p) { return p.then( result => { console.log(name, "initial resolution:", result); return result; }, error => { console.log(name, "initial rejection; propagating it"); throw error; } ) .finally(() => { console.log(name, "in finally"); }) .then( result => { console.log(name, "resolved:", result); }, error => { console.log(name, "rejected:", error); } ); }
you've said want without either extending promise.prototype
or subclassing. in es5, utility function extremely awkward use, because you'd have pass promise act on, out of step normal promise usage. in es2015, it's possible more natural it's still more of pain call either modifying prototype or subclassing:
let = (() => { let worker = (f, done) => { return promise.resolve(f()).then(done, done); }; return function always(f) { return [ result => worker(f, () => result), error => worker(f, () => { throw error; }) ]; } })();
usage:
thepromise.then(...always(/*..your function..*/)).
note use of spread operator (which why won't work in es5), always
can supply both arguments then
.
example:
let = (() => { let worker = (f, done) => { return promise.resolve(f()).then(done, done); }; return function always(f) { return [ result => worker(f, () => result), error => worker(f, () => { throw error; }) ]; } })(); test("p1", promise.resolve("good")).then(...always( () => { test("p2", promise.reject("bad")); } )); function test(name, p) { return p.then( result => { console.log(name, "initial resolution:", result); return result; }, error => { console.log(name, "initial rejection; propagating it"); throw error; } ) .then(...always(() => { console.log(name, "in finally"); })) .then( result => { console.log(name, "resolved:", result); }, error => { console.log(name, "rejected:", error); } ); }
in comments expressed concern finally
wouldn't wait promise; here's last always
example again, delays demonstrate does:
let = (() => { let worker = (f, done) => { return promise.resolve(f()).then(done, done); }; return function always(f) { return [ result => worker(f, () => result), error => worker(f, () => { throw error; }) ]; } })(); test("p1", 500, false, "good").then(...always( () => { test("p2", 500, true, "bad"); } )); function test(name, delay, fail, value) { // make our test promise let p = new promise((resolve, reject) => { console.log(name, `created ${delay}ms delay before settling`); settimeout(() => { if (fail) { console.log(name, "rejecting"); reject(value); } else { console.log(name, "resolving"); resolve(value); } }, delay); }); // use return p.then( result => { console.log(name, "initial resolution:", result); return result; }, error => { console.log(name, "initial rejection; propagating it"); throw error; } ) .then(...always(() => { console.log(name, "in finally"); })) .then( result => { console.log(name, "resolved:", result); }, error => { console.log(name, "rejected:", error); } ); }
Comments
Post a Comment