promise.prototype.finally
Advanced tools
Comparing version 1.0.0 to 1.0.1
@@ -8,25 +8,24 @@ /** | ||
// Get a handle on the global object | ||
var local; | ||
if (typeof global !== 'undefined') local = global; | ||
else if (typeof window !== 'undefined' && window.document) local = window; | ||
else local = self; | ||
(function() { | ||
// Get a handle on the global object | ||
var local; | ||
if (typeof global !== 'undefined') local = global; | ||
else if (typeof window !== 'undefined' && window.document) local = window; | ||
else local = self; | ||
// Polyfill (verb) | ||
var finallySupport = "finally" in local.Promise.prototype; | ||
if (!finallySupport) local.Promise.prototype['finally'] = finallyPolyfill; | ||
// It's replaced unconditionally to preserve the expected behavior | ||
// in programs even if there's ever a native finally. | ||
local.Promise.prototype['finally'] = function finallyPolyfill(callback) { | ||
var constructor = this.constructor; | ||
// Polyfill (noun) | ||
function finallyPolyfill(callback) { | ||
var constructor = this.constructor; | ||
return this.then(function(value) { | ||
return constructor.resolve(callback()).then(function() { | ||
return value; | ||
return this.then(function(value) { | ||
return constructor.resolve(callback()).then(function() { | ||
return value; | ||
}); | ||
}, function(reason) { | ||
return constructor.resolve(callback()).then(function() { | ||
throw reason; | ||
}); | ||
}); | ||
}, function(reason) { | ||
return constructor.resolve(callback()).then(function() { | ||
throw reason; | ||
}); | ||
}); | ||
} | ||
}; | ||
}()); |
{ | ||
"name": "promise.prototype.finally", | ||
"description": "A polyfill for Promise.prototype.finally for ES6 compliant promises", | ||
"version": "1.0.0", | ||
"version": "1.0.1", | ||
"scripts": { | ||
@@ -6,0 +6,0 @@ "test": "npm run lintspaces && npm run jshint && ./node_modules/.bin/mocha test", |
# Promise.prototype.finally [ ![Codeship Status for matthew-andrews/Promise.prototype.finally](https://codeship.io/projects/d2ed45d0-3364-0132-3c6b-26237c03a86a/status)](https://codeship.io/projects/40589) | ||
A polyfill for `Promise.prototype.finally`. See docs on what finally is on [your favourite pre-ES6 promise library](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback). | ||
A polyfill for `Promise.prototype.finally`. See docs on what finally is on [your favourite pre-ES6 promise library](https://github.com/tildeio/rsvp.js/blob/master/README.md#finally). | ||
@@ -36,2 +36,2 @@ Warning: This micro-library doesn't force you to use any particular Promise implementation by using whatever `Promise` has been defined as globally. This is so that you may use any ES6 standard Promise compliant library - or, of course, native ES6 Promises. | ||
The lead developer of **Promise.prototype.finally** is [Matt Andrews](http://twitter.com/andrewsmatt) at FT with unwitting help and support from [Stefan Penner](https://twitter.com/stefanpenner). All open source code released by FT Labs is licenced under the MIT licence. We welcome comments, feedback and suggestions. Please feel free to raise an issue or pull request. | ||
The lead developer of **Promise.prototype.finally** is [Matt Andrews](http://twitter.com/andrewsmatt) at FT, the code is based on [a snippet](https://github.com/domenic/promises-unwrapping/issues/18#issuecomment-57801572) by [Stefan Penner](https://twitter.com/stefanpenner) - used [with permission](https://twitter.com/stefanpenner/status/520904347073667072). All open source code released by FT Labs is licenced under the MIT licence. We welcome comments, feedback and suggestions. Please feel free to raise an issue or pull request. |
@@ -6,21 +6,156 @@ var assert = require('assert'); | ||
describe('finally', function() { | ||
it('is defined', function(done) { | ||
assert('finally' in Promise.prototype); | ||
done(); | ||
}); | ||
it('is called on resolve', function(done) { | ||
Promise.resolve(3) | ||
.finally(function(value) { | ||
assert(value === undefined); | ||
done(); | ||
var o_create = Object.create || function(o, props) { | ||
function F() {} | ||
F.prototype = o; | ||
if (typeof(props) === "object") { | ||
for (var prop in props) { | ||
if (props.hasOwnProperty((prop))) { | ||
F[prop] = props[prop]; | ||
} | ||
} | ||
} | ||
return new F(); | ||
}; | ||
describe("Promise.finally", function() { | ||
describe("native finally behaviour", function() { | ||
describe("no value is passed in", function() { | ||
it("does not provide a value to the finally code", function(done) { | ||
var fulfillmentValue = 1; | ||
var promise = Promise.resolve(fulfillmentValue); | ||
promise['finally'](function() { | ||
assert.equal(arguments.length, 0); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it('is called on reject', function(done) { | ||
Promise.reject(3) | ||
.finally(function(value) { | ||
assert(value === undefined); | ||
done(); | ||
it("does not provide a reason to the finally code", function(done) { | ||
var rejectionReason = new Error(); | ||
var promise = Promise.reject(rejectionReason); | ||
promise['finally'](function(arg) { | ||
assert.equal(arguments.length, 0); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe("non-exceptional cases do not affect the result", function(){ | ||
it("preserves the original fulfillment value even if the finally callback returns a value", function(done) { | ||
var fulfillmentValue = 1; | ||
var promise = Promise.resolve(fulfillmentValue); | ||
promise['finally'](function() { | ||
return 2; | ||
}).then(function(value) { | ||
assert.equal(fulfillmentValue, value); | ||
done(); | ||
}); | ||
}); | ||
it("preserves the original rejection reason even if the finally callback returns a value", function(done) { | ||
var rejectionReason = new Error(); | ||
var promise = Promise.reject(rejectionReason); | ||
promise['finally'](function() { | ||
return 2; | ||
}).then(undefined, function(reason) { | ||
assert.equal(rejectionReason, reason); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe("exception cases do propogate the failure", function(){ | ||
describe("fulfilled promise", function(){ | ||
it("propagates changes via throw", function(done) { | ||
var promise = Promise.resolve(1); | ||
var expectedReason = new Error(); | ||
promise['finally'](function() { | ||
throw expectedReason; | ||
}).then(undefined, function(reason) { | ||
assert.deepEqual(expectedReason, reason); | ||
done(); | ||
}); | ||
}); | ||
it("propagates changes via returned rejected promise", function(done){ | ||
var promise = Promise.resolve(1); | ||
var expectedReason = new Error(); | ||
promise['finally'](function() { | ||
return Promise.reject(expectedReason); | ||
}).then(undefined, function(reason) { | ||
assert.deepEqual(expectedReason, reason); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe("rejected promise", function(){ | ||
it("propagates changes via throw", function(done) { | ||
var promise = Promise.reject(1); | ||
var expectedReason = new Error(); | ||
promise['finally'](function() { | ||
throw expectedReason; | ||
}).then(undefined, function(reason) { | ||
assert.deepEqual(expectedReason, reason); | ||
done(); | ||
}); | ||
}); | ||
it("propagates changes via returned rejected promise", function(done){ | ||
var promise = Promise.reject(1); | ||
var expectedReason = new Error(); | ||
promise['finally'](function() { | ||
return Promise.reject(expectedReason); | ||
}).then(undefined, function(reason) { | ||
assert.deepEqual(expectedReason, reason); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe("inheritance", function() { | ||
function Subclass (resolver) { | ||
this._promise$constructor(resolver); | ||
} | ||
Subclass.prototype = o_create(Promise.prototype); | ||
Subclass.prototype.constructor = Subclass; | ||
Subclass.prototype._promise$constructor = Promise; | ||
Subclass.resolve = Promise.resolve; | ||
Subclass.reject = Promise.reject; | ||
Subclass.all = Promise.all; | ||
it("preserves correct subclass when chained", function() { | ||
var promise = Subclass.resolve().finally(); | ||
assert.ok(promise instanceof Subclass); | ||
assert.equal(promise.constructor, Subclass); | ||
}); | ||
it("preserves correct subclass when rejected", function() { | ||
var promise = Subclass.resolve().finally(function() { | ||
throw new Error("OMG"); | ||
}); | ||
assert.ok(promise instanceof Subclass); | ||
assert.equal(promise.constructor, Subclass); | ||
}); | ||
it("preserves correct subclass when someone returns a thenable", function() { | ||
var promise = Subclass.resolve().finally(function() { | ||
return Promise.resolve(1); | ||
}); | ||
assert.ok(promise instanceof Subclass); | ||
assert.equal(promise.constructor, Subclass); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); |
8458
162