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.constructorwe're callingresolveon 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
finallycallback, , no indication of whether promise resolved or rejected, in order consistentfinallyin classictry-catch-finallystructure. if 1 wanted, 1 pass of information callback.similarly, above not use value returned
finallycallback 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