Comparing version 2.2.1 to 2.3.0
@@ -0,1 +1,8 @@ | ||
### 2.3.0 | ||
* New [`promise.tap`](docs/api.md#tap) for adding side effects to a promise chain. | ||
* New `MessageChannel` scheduler reduces "time-to-first" handler, in environments that support it. | ||
* Performance optimizations for promise resolution. | ||
* Internal architecture improvements to pave the way for when.js 3.0.0. | ||
### 2.2.1 | ||
@@ -2,0 +9,0 @@ |
@@ -16,5 +16,5 @@ /** @license MIT License (c) copyright 2010-2013 original author or authors */ | ||
function Monitor(parent) { | ||
if(!(this instanceof Monitor)) { | ||
return new Monitor(parent); | ||
function PromiseStatus(parent) { | ||
if(!(this instanceof PromiseStatus)) { | ||
return new PromiseStatus(parent); | ||
} | ||
@@ -38,3 +38,3 @@ | ||
Monitor.prototype = { | ||
PromiseStatus.prototype = { | ||
observed: function () { | ||
@@ -46,3 +46,3 @@ if(this.key in promises) { | ||
return new Monitor(this); | ||
return new PromiseStatus(this); | ||
}, | ||
@@ -79,3 +79,3 @@ fulfilled: function () { | ||
function publish(target) { | ||
target.PromiseStatus = Monitor; | ||
target.PromiseStatus = PromiseStatus; | ||
target.reportUnhandled = report; | ||
@@ -82,0 +82,0 @@ target.resetUnhandled = reset; |
{ | ||
"name": "when", | ||
"version": "2.2.1", | ||
"version": "2.3.0", | ||
"description": "A lightweight Promises/A+ and when() implementation, plus other async goodies.", | ||
@@ -53,4 +53,4 @@ "keywords": ["Promises/A+", "promises-aplus", "promise", "promises", "deferred", "deferreds", "when", "async", "asynchronous", "cujo"], | ||
"scripts": { | ||
"test": "jshint . && buster test -e node && promises-aplus-tests test/promises-aplus-adapter.js", | ||
"ci": "npm test", | ||
"test": "jshint . && buster test -e node -r specification && promises-aplus-tests test/promises-aplus-adapter.js", | ||
"ci": "npm test && sauceme", | ||
"tunnel": "sauceme -m", | ||
@@ -57,0 +57,0 @@ "start": "buster static -e browser", |
@@ -14,6 +14,13 @@ <a href="http://promises-aplus.github.com/promises-spec"><img src="http://promises-aplus.github.com/promises-spec/assets/logo-small.png" alt="Promises/A+ logo" align="right" /></a> | ||
It passes the [Promises/A+ Test Suite](https://github.com/promises-aplus/promises-tests), is [very fast](https://github.com/cujojs/promise-perf-tests#test-results), is under 1.5k when compiled with Google Closure + gzip, and has no external dependencies. | ||
It passes the [Promises/A+ Test Suite](https://github.com/promises-aplus/promises-tests), is [very fast](https://github.com/cujojs/promise-perf-tests#test-results) and compact, and has no external dependencies. | ||
# What's New? | ||
### 2.3.0 | ||
* New [`promise.tap`](docs/api.md#tap) for adding side effects to a promise chain. | ||
* New `MessageChannel` scheduler reduces "time-to-first" handler, in environments that support it. | ||
* Performance optimizations for promise resolution. | ||
* Internal architecture improvements to pave the way for when.js 3.0.0. | ||
### 2.2.1 | ||
@@ -20,0 +27,0 @@ |
267
when.js
@@ -12,3 +12,3 @@ /** @license MIT License (c) copyright 2011-2013 original author or authors */ | ||
* @author John Hann | ||
* @version 2.2.1 | ||
* @version 2.3.0 | ||
*/ | ||
@@ -37,3 +37,2 @@ (function(define, global) { 'use strict'; | ||
/** | ||
@@ -65,6 +64,8 @@ * Register an observer for a promise or immediate value. | ||
* @constructor | ||
* @param {function} sendMessage function to deliver messages to the promise's handler | ||
* @param {function?} inspect function that reports the promise's state | ||
* @name Promise | ||
*/ | ||
function Promise(then, inspect) { | ||
this.then = then; | ||
function Promise(sendMessage, inspect) { | ||
this._message = sendMessage; | ||
this.inspect = inspect; | ||
@@ -116,2 +117,12 @@ } | ||
/** | ||
* Runs a side effect when this promise fulfills, without changing the | ||
* fulfillment value. | ||
* @param {function} onFulfilledSideEffect | ||
* @returns {Promise} | ||
*/ | ||
tap: function(onFulfilledSideEffect) { | ||
return this.then(onFulfilledSideEffect)['yield'](this); | ||
}, | ||
/** | ||
* Assumes that this promise will fulfill with an array, and arranges | ||
@@ -244,5 +255,6 @@ * for the onFulfilled to be called with the array as its argument list | ||
function _promise(resolver, status) { | ||
var self, value, handlers = []; | ||
var self, value, consumers = []; | ||
self = new Promise(then, inspect); | ||
self = new Promise(_message, inspect); | ||
self.then = then; | ||
@@ -259,3 +271,22 @@ // Call the provider resolver to seal the promise's fate | ||
function _message(type, args, resolve, notify) { | ||
consumers ? consumers.push(deliver) : enqueue(function() { deliver(value); }); | ||
function deliver(p) { | ||
p._message(type, args, resolve, notify); | ||
} | ||
} | ||
/** | ||
* Returns a snapshot of the promise's state at the instant inspect() | ||
* is called. The returned object is not live and will not update as | ||
* the promise's state changes. | ||
* @returns {{ state:String, value?:*, reason?:* }} status snapshot | ||
* of the promise. | ||
*/ | ||
function inspect() { | ||
return value ? value.inspect() : toPendingState(); | ||
} | ||
/** | ||
* Register handlers for this promise. | ||
@@ -268,21 +299,9 @@ * @param [onFulfilled] {Function} fulfillment handler | ||
function then(onFulfilled, onRejected, onProgress) { | ||
var next = _promise(function(resolve, reject, notify) { | ||
// if not resolved, push onto handlers, otherwise execute asap | ||
// but not in the current stack | ||
handlers ? handlers.push(run) : enqueue(function() { run(value); }); | ||
function run(p) { | ||
p.then(onFulfilled, onRejected, onProgress) | ||
.then(resolve, reject, notify); | ||
} | ||
/*jshint unused:false*/ | ||
var args = arguments; | ||
return _promise(function(resolve, reject, notify) { | ||
_message('when', args, resolve, notify); | ||
}, status && status.observed()); | ||
return next; | ||
} | ||
function inspect() { | ||
return value ? value.inspect() : toPendingState(); | ||
} | ||
/** | ||
@@ -294,3 +313,3 @@ * Transition from pre-resolution state to post-resolution state, notifying | ||
function promiseResolve(val) { | ||
if(!handlers) { | ||
if(!consumers) { | ||
return; | ||
@@ -300,10 +319,7 @@ } | ||
value = coerce(val); | ||
scheduleHandlers(handlers, value); | ||
handlers = undef; | ||
scheduleConsumers(consumers, value); | ||
consumers = undef; | ||
if (status) { | ||
value.then( | ||
function () { status.fulfilled(); }, | ||
function(r) { status.rejected(r); } | ||
); | ||
if(status) { | ||
updateStatus(value, status); | ||
} | ||
@@ -325,4 +341,4 @@ } | ||
function promiseNotify(update) { | ||
if(handlers) { | ||
scheduleHandlers(handlers, progressing(update)); | ||
if(consumers) { | ||
scheduleConsumers(consumers, progressed(update)); | ||
} | ||
@@ -333,2 +349,63 @@ } | ||
/** | ||
* Creates a fulfilled, local promise as a proxy for a value | ||
* NOTE: must never be exposed | ||
* @param {*} value fulfillment value | ||
* @returns {Promise} | ||
*/ | ||
function fulfilled(value) { | ||
return near( | ||
new NearFulfilledProxy(value), | ||
function() { return toFulfilledState(value); } | ||
); | ||
} | ||
/** | ||
* Creates a rejected, local promise with the supplied reason | ||
* NOTE: must never be exposed | ||
* @param {*} reason rejection reason | ||
* @returns {Promise} | ||
*/ | ||
function rejected(reason) { | ||
return near( | ||
new NearRejectedProxy(reason), | ||
function() { return toRejectedState(reason); } | ||
); | ||
} | ||
/** | ||
* Creates a near promise using the provided proxy | ||
* NOTE: must never be exposed | ||
* @param {object} proxy proxy for the promise's ultimate value or reason | ||
* @param {function} inspect function that returns a snapshot of the | ||
* returned near promise's state | ||
* @returns {Promise} | ||
*/ | ||
function near(proxy, inspect) { | ||
return new Promise(function(type, args, resolve) { | ||
try { | ||
resolve(proxy[type].apply(proxy, args)); | ||
} catch(e) { | ||
resolve(rejected(e)); | ||
} | ||
}, inspect); | ||
} | ||
/** | ||
* Create a progress promise with the supplied update. | ||
* @private | ||
* @param {*} update | ||
* @return {Promise} progress promise | ||
*/ | ||
function progressed(update) { | ||
return new Promise(function (type, args, _, notify) { | ||
var onProgress = args[2]; | ||
try { | ||
notify(typeof onProgress === 'function' ? onProgress(update) : update); | ||
} catch(e) { | ||
notify(e); | ||
} | ||
}); | ||
} | ||
/** | ||
* Coerces x to a trusted Promise | ||
@@ -338,3 +415,3 @@ * | ||
* @param {*} x thing to coerce | ||
* @returns {Promise} Guaranteed to return a trusted Promise. If x | ||
* @returns {*} Guaranteed to return a trusted Promise. If x | ||
* is trusted, returns x, otherwise, returns a new, trusted, already-resolved | ||
@@ -377,62 +454,31 @@ * Promise whose resolution value is: | ||
/** | ||
* Create an already-fulfilled promise for the supplied value | ||
* @private | ||
* Proxy for a near, fulfilled value | ||
* @param {*} value | ||
* @return {Promise} fulfilled promise | ||
* @constructor | ||
*/ | ||
function fulfilled(value) { | ||
var self = new Promise(function (onFulfilled) { | ||
try { | ||
return typeof onFulfilled == 'function' | ||
? coerce(onFulfilled(value)) : self; | ||
} catch (e) { | ||
return rejected(e); | ||
} | ||
}, function() { | ||
return toFulfilledState(value); | ||
}); | ||
return self; | ||
function NearFulfilledProxy(value) { | ||
this.value = value; | ||
} | ||
NearFulfilledProxy.prototype.when = function(onResult) { | ||
return typeof onResult === 'function' ? onResult(this.value) : this.value; | ||
}; | ||
/** | ||
* Create an already-rejected promise with the supplied rejection reason. | ||
* @private | ||
* @param {*} reason | ||
* @return {Promise} rejected promise | ||
* Proxy for a near rejection | ||
* @param {*} value | ||
* @constructor | ||
*/ | ||
function rejected(reason) { | ||
var self = new Promise(function (_, onRejected) { | ||
try { | ||
return typeof onRejected == 'function' | ||
? coerce(onRejected(reason)) : self; | ||
} catch (e) { | ||
return rejected(e); | ||
} | ||
}, function() { | ||
return toRejectedState(reason); | ||
}); | ||
return self; | ||
function NearRejectedProxy(reason) { | ||
this.reason = reason; | ||
} | ||
/** | ||
* Create a progress promise with the supplied update. | ||
* @private | ||
* @param {*} update | ||
* @return {Promise} progress promise | ||
*/ | ||
function progressing(update) { | ||
var self = new Promise(function (_, __, onProgress) { | ||
try { | ||
return typeof onProgress == 'function' | ||
? progressing(onProgress(update)) : self; | ||
} catch (e) { | ||
return progressing(e); | ||
} | ||
}); | ||
NearRejectedProxy.prototype.when = function(_, onError) { | ||
if(typeof onError === 'function') { | ||
return onError(this.reason); | ||
} else { | ||
throw this.reason; | ||
} | ||
}; | ||
return self; | ||
} | ||
/** | ||
@@ -445,3 +491,3 @@ * Schedule a task that will process a list of handlers | ||
*/ | ||
function scheduleHandlers(handlers, value) { | ||
function scheduleConsumers(handlers, value) { | ||
enqueue(function() { | ||
@@ -455,2 +501,9 @@ var handler, i = 0; | ||
function updateStatus(value, status) { | ||
value._message('when', [ | ||
function () { status.fulfilled(); }, | ||
function (r) { status.rejected(r); } | ||
], identity, identity); | ||
} | ||
/** | ||
@@ -650,2 +703,3 @@ * Determines if promiseOrValue is a promise or not | ||
results[i] = mapped; | ||
notify(mapped); | ||
@@ -655,3 +709,3 @@ if(!--toResolve) { | ||
} | ||
}, reject, notify); | ||
}, reject); | ||
} | ||
@@ -729,3 +783,3 @@ } | ||
// | ||
// Utilities, etc. | ||
// Internals, utilities, etc. | ||
// | ||
@@ -771,6 +825,5 @@ | ||
// | ||
// Capture function and array utils | ||
// | ||
/*global setImmediate,process,vertx*/ | ||
// capture setTimeout to avoid being caught by fake timers | ||
// used in time based tests | ||
setTimeout = global.setTimeout; | ||
@@ -780,10 +833,23 @@ // Allow attaching the monitor to when() if env has no console | ||
// capture setTimeout to avoid being caught by fake timers used in time based tests | ||
setTimeout = global.setTimeout; | ||
// Prefer setImmediate, cascade to node, vertx and finally setTimeout | ||
nextTick = typeof setImmediate === 'function' ? setImmediate.bind(global) | ||
: typeof process === 'object' && process.nextTick ? process.nextTick | ||
: typeof vertx === 'object' ? vertx.runOnLoop // vert.x | ||
: function(task) { setTimeout(task, 0); }; // fallback | ||
// Prefer setImmediate or MessageChannel, cascade to node, | ||
// vertx and finally setTimeout | ||
/*global setImmediate,MessageChannel,process,vertx*/ | ||
if (typeof setImmediate === 'function') { | ||
nextTick = setImmediate.bind(global); | ||
} else if(typeof MessageChannel !== 'undefined') { | ||
var channel = new MessageChannel(); | ||
channel.port1.onmessage = drainQueue; | ||
nextTick = function() { channel.port2.postMessage(0); }; | ||
} else if (typeof process === 'object' && process.nextTick) { | ||
nextTick = process.nextTick; | ||
} else if (typeof vertx === 'object') { | ||
nextTick = vertx.runOnLoop; | ||
} else { | ||
nextTick = function(t) { setTimeout(t, 0); }; | ||
} | ||
// | ||
// Capture/polyfill function and array utils | ||
// | ||
// Safe function calls | ||
@@ -854,5 +920,2 @@ funcProto = Function.prototype; | ||
}); | ||
})( | ||
typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(); }, | ||
this | ||
); | ||
})(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(); }, this); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
124695
3170
142