Comparing version 1.5.0 to 1.5.1
269
debug.js
@@ -46,7 +46,8 @@ /** @license MIT License (c) copyright B Cavalier & J Hann */ | ||
var promiseId, freeze, pending, exceptionsToRethrow, undef; | ||
var promiseId, pending, exceptionsToRethrow, own, debugProp, undef; | ||
promiseId = 0; | ||
freeze = Object.freeze || function(o) { return o; }; | ||
pending = {}; | ||
own = Object.prototype.hasOwnProperty; | ||
debugProp = 'whendebug'; | ||
@@ -61,99 +62,74 @@ exceptionsToRethrow = { | ||
/** | ||
* Replacement for when() that sets up debug logging on the | ||
* returned promise. | ||
*/ | ||
function whenDebug() { | ||
return debugPromise(when.apply(null, wrapCallbacks(arguments))); | ||
} | ||
/** | ||
* Setup debug output handlers for the supplied promise. | ||
* @param p {Promise} A trusted (when.js) promise | ||
* @return p | ||
* @return {Promise} a new promise that outputs debug info and | ||
* has a useful toString | ||
*/ | ||
function debugPromise(p) { | ||
// TODO: Need to find a way for promises returned by .then() | ||
// to also be debug promises. | ||
p.then( | ||
undef, | ||
function(err) { | ||
if(p.id) { | ||
console.error(p.toString()); | ||
} else { | ||
console.error('[object Promise] REJECTED:', err); | ||
} | ||
function debugPromise(p, parent) { | ||
var id, origThen, newPromise, logReject; | ||
return when.reject(err); | ||
} | ||
); | ||
if(own.call(p, debugProp)) { | ||
return p; | ||
} | ||
return p; | ||
} | ||
promiseId++; | ||
id = (parent && 'id' in parent) ? (parent.id + '.' + promiseId) : promiseId; | ||
function wrapCallback(cb) { | ||
if(typeof cb != 'function') { | ||
return cb; | ||
} | ||
origThen = p.then; | ||
newPromise = beget(p); | ||
newPromise.id = id; | ||
newPromise.parent = parent; | ||
newPromise[debugProp] = true; | ||
return function(v) { | ||
try { | ||
return cb(v); | ||
} catch(err) { | ||
if(err) { | ||
if (err.name in exceptionsToRethrow) { | ||
setTimeout(function() { | ||
throw err; | ||
}, 0); | ||
} else if (err.stack) { | ||
console.error(err.stack); | ||
} | ||
} | ||
newPromise.toString = function() { | ||
return toString('Promise', id); | ||
}; | ||
throw err; | ||
newPromise.then = function(cb, eb) { | ||
if(typeof eb === 'function') { | ||
var promise = newPromise; | ||
do { | ||
promise.handled = true; | ||
} while((promise = promise.parent) && !promise.handled); | ||
} | ||
return debugPromise(origThen.apply(p, wrapCallbacks(arguments)), newPromise); | ||
}; | ||
} | ||
function wrapCallbacks(callbacks) { | ||
var cb, args, len, i; | ||
logReject = function() { | ||
console.error(newPromise.toString()); | ||
}; | ||
args = []; | ||
p.then( | ||
function(val) { | ||
newPromise.toString = function() { | ||
return toString('Promise', id, 'resolved', val); | ||
}; | ||
return val; | ||
}, | ||
wrapCallback(function(err) { | ||
newPromise.toString = function() { | ||
return toString('Promise', id, 'REJECTED', err); | ||
}; | ||
for(i = 0, len = callbacks.length; i < len; i++) { | ||
args[i] = typeof (cb = callbacks[i]) == 'function' | ||
? wrapCallback(cb) | ||
: cb; | ||
} | ||
if(!newPromise.handled) { | ||
logReject(); | ||
} | ||
return args; | ||
} | ||
throw err; | ||
}) | ||
); | ||
/** | ||
* Helper to form debug string for promises depending on their | ||
* current state | ||
* @param name | ||
* @param id | ||
* @param status | ||
* @param value | ||
*/ | ||
function toString(name, id, status, value) { | ||
var s = '[object ' + name + ' ' + id + '] ' + status; | ||
if(value !== pending) { | ||
s += ': ' + value; | ||
} | ||
return s; | ||
return newPromise; | ||
} | ||
function F() {} | ||
function beget(o) { | ||
F.prototype = o; | ||
o = new F(); | ||
F.prototype = undef; | ||
return o; | ||
} | ||
/** | ||
* Replacement for when() that sets up debug logging on the | ||
* returned promise. | ||
*/ | ||
function whenDebug() { | ||
return debugPromise(when.apply(null, wrapCallbacks(arguments))); | ||
} | ||
/** | ||
* Replacement for when.defer() that sets up debug logging | ||
@@ -184,6 +160,5 @@ * on the created Deferred, its resolver, and its promise. | ||
// and deferred | ||
d.promise = beget(d.promise); | ||
d.promise.toString = function() { | ||
return toString('Promise', id, status, value); | ||
}; | ||
origThen = d.promise.then; | ||
d.id = id; | ||
d.promise = debugPromise(d.promise, d); | ||
@@ -201,3 +176,3 @@ d.resolver = beget(d.resolver); | ||
return origProgress.apply(undef, arguments); | ||
return origProgress(update); | ||
}; | ||
@@ -237,36 +212,11 @@ | ||
// Experimenting with setting up ways to also debug promises returned | ||
// by .then(). Also need to find a way to extend the id in a way that | ||
// makes it obvious the returned promise is NOT the original, but is | ||
// related to it--it's downstream in the promise chain. | ||
origThen = d.promise.then; | ||
d.then = d.promise.then = function(cb, eb, pb) { | ||
d.then = d.promise.then; | ||
var id = d.id + '>' + (++promiseId); | ||
var p = origThen.apply(null, wrapCallbacks(arguments)); | ||
p.id = id; | ||
p = beget(p); | ||
p.toString = function() { | ||
return toString('Promise', p.id, status, value); | ||
}; | ||
// See below. Not sure if debug promises should be frozen | ||
return freeze(p); | ||
}; | ||
// Add an id to all directly created promises. It'd be great | ||
// to find a way to propagate this id to promise created by .then() | ||
d.id = d.promise.id = d.resolver.id = id; | ||
d.resolver.id = id; | ||
// Attach debug handlers after the substitute promise | ||
// has been setup, so the id can be logged. | ||
//debugPromise(d.promise); | ||
// TODO: Should we still freeze these? | ||
// Seems safer for now to err on the side of caution and freeze them, | ||
// but it could be useful to all them to be modified during debugging. | ||
freeze(d.promise); | ||
freeze(d.resolver); | ||
// freeze(d.promise); | ||
// freeze(d.resolver); | ||
@@ -276,15 +226,5 @@ return d; | ||
function alreadyResolved() { | ||
throw new Error('already completed'); | ||
} | ||
whenDebug.defer = deferDebug; | ||
whenDebug.isPromise = when.isPromise; | ||
function makeDebug(name, func) { | ||
whenDebug[name] = function() { | ||
return debugPromise(func.apply(when, arguments)); | ||
}; | ||
} | ||
// For each method we haven't already replaced, replace it with | ||
@@ -300,2 +240,81 @@ // one that sets up debug logging on the returned promise | ||
// Wrap result of when[name] in a debug promise | ||
function makeDebug(name, func) { | ||
whenDebug[name] = function() { | ||
return debugPromise(func.apply(when, arguments)); | ||
}; | ||
} | ||
// Wrap a promise callback to catch exceptions and log or | ||
// rethrow as uncatchable | ||
function wrapCallback(cb) { | ||
if(typeof cb != 'function') { | ||
return cb; | ||
} | ||
return function(v) { | ||
try { | ||
return cb(v); | ||
} catch(err) { | ||
throwUncatchableIfNecessary(err); | ||
throw err; | ||
} | ||
}; | ||
} | ||
// Wrap a callback, errback, progressback tuple | ||
function wrapCallbacks(callbacks) { | ||
var cb, args, len, i; | ||
args = []; | ||
for(i = 0, len = callbacks.length; i < len; i++) { | ||
args[i] = typeof (cb = callbacks[i]) == 'function' | ||
? wrapCallback(cb) | ||
: cb; | ||
} | ||
return args; | ||
} | ||
// Stringify a promise, deferred, or resolver | ||
function toString(name, id, status, value) { | ||
var s = '[object ' + name + ' ' + id + ']'; | ||
if(arguments.length > 2) { | ||
s += ' ' + status; | ||
if(value !== pending) { | ||
s += ': ' + value; | ||
} | ||
} | ||
return s; | ||
} | ||
function throwUncatchableIfNecessary(err) { | ||
if (err && err.name in exceptionsToRethrow) { | ||
setTimeout(function() { | ||
throw err; | ||
}, 0); | ||
} | ||
} | ||
// Helper to invoke when resolve/reject/progress is called on | ||
// an already-resolved deferred or resolver | ||
function alreadyResolved() { | ||
throw new Error('already completed'); | ||
} | ||
// The usual Crockford | ||
function F() {} | ||
function beget(o) { | ||
F.prototype = o; | ||
o = new F(); | ||
F.prototype = undef; | ||
return o; | ||
} | ||
}); | ||
@@ -302,0 +321,0 @@ })(typeof define == 'function' |
{ | ||
"name": "when", | ||
"version": "1.5.0", | ||
"version": "1.5.1", | ||
"description": "A lightweight Promise and when() implementation, plus other async goodies.", | ||
"keywords": ["promises", "when", "async", "cujo"], | ||
"keywords": ["promise", "promises", "deferred", "deferreds", "when", "async", "asynchronous", "cujo"], | ||
"licenses": [ | ||
@@ -7,0 +7,0 @@ { |
@@ -12,2 +12,7 @@ # when.js [![Build Status](https://secure.travis-ci.org/cujojs/when.png)](http://travis-ci.org/cujojs/when) | ||
### 1.5.1 | ||
* Performance optimization for [when.defer](when/blob/master/docs/api.md#whendefer), up to 1.5x in some cases. | ||
* [when/debug](when/blob/master/docs/api.md#whendebug) can now log exceptions and rejections in deeper promise chains, in some cases, even when the promises involved aren't when.js promises. | ||
### 1.5.0 | ||
@@ -14,0 +19,0 @@ |
156
when.js
@@ -10,7 +10,7 @@ /** @license MIT License (c) copyright B Cavalier & J Hann */ | ||
* | ||
* @version 1.5.0 | ||
* @version 1.5.1 | ||
*/ | ||
(function(define, global) { | ||
define(['module'], function(module) { "use strict"; | ||
(function(define, global) { 'use strict'; | ||
define(['module'], function(module) { | ||
var freeze, reduceArray, slice, envFreeze, falseRx, undef; | ||
@@ -109,3 +109,3 @@ | ||
// See: https://github.com/kriskowal/q/issues/106 | ||
if (promiseOrValue != null && typeof promiseOrValue.valueOf === "function") { | ||
if (promiseOrValue != null && typeof promiseOrValue.valueOf === 'function') { | ||
promiseOrValue = promiseOrValue.valueOf(); | ||
@@ -157,3 +157,5 @@ } | ||
*/ | ||
function Promise() {} | ||
function Promise(then) { | ||
this.then = then; | ||
} | ||
@@ -193,6 +195,3 @@ Promise.prototype = freeze({ | ||
function resolved(value) { | ||
var p = new Promise(); | ||
p.then = function(callback) { | ||
var p = new Promise(function(callback) { | ||
try { | ||
@@ -203,3 +202,3 @@ return resolve(callback ? callback(value) : value); | ||
} | ||
}; | ||
}); | ||
@@ -218,6 +217,3 @@ return freeze(p); | ||
function rejected(reason) { | ||
var p = new Promise(); | ||
p.then = function(callback, errback) { | ||
var p = new Promise(function(callback, errback) { | ||
try { | ||
@@ -228,4 +224,4 @@ return errback ? resolve(errback(reason)) : rejected(reason); | ||
} | ||
}; | ||
}); | ||
return freeze(p); | ||
@@ -246,7 +242,10 @@ } | ||
function defer() { | ||
var deferred, promise, resolver, listeners, progressHandlers, | ||
var deferred, promise, listeners, progressHandlers, | ||
_then, _progress, _resolve; | ||
listeners = []; | ||
progressHandlers = []; | ||
/** | ||
* The promise for the new deferred | ||
* @type {Promise} | ||
*/ | ||
promise = new Promise(then); | ||
@@ -258,75 +257,21 @@ /** | ||
*/ | ||
deferred = {}; | ||
deferred = { | ||
then: then, | ||
resolve: promiseResolve, | ||
reject: promiseReject, | ||
progress: promiseProgress, | ||
promise: freeze(promise), | ||
/** | ||
* The {@link Resolver} for this {@link Deferred} | ||
* @memberOf Deferred | ||
* @name resolver | ||
* @class Resolver | ||
*/ | ||
resolver = {}; | ||
/** | ||
* The {@link Promise} for this {@link Deferred} | ||
* @memberOf Deferred | ||
* @name promise | ||
* @type {Promise} | ||
*/ | ||
promise = new Promise(); | ||
/** | ||
* Registers a handler for this {@link Deferred}'s {@link Promise}. Even though all arguments | ||
* are optional, each argument that *is* supplied must be null, undefined, or a Function. | ||
* Any other value will cause an Error to be thrown. | ||
* @memberOf Promise | ||
* @name then | ||
* @param [callback] {Function} resolution handler | ||
* @param [errback] {Function} rejection handler | ||
* @param [progback] {Function} progress handler | ||
* @throw {Error} if any argument is not null, undefined, or a Function | ||
*/ | ||
promise.then = deferred.then = function then(callback, errback, progback) { | ||
return _then(callback, errback, progback); | ||
resolver: freeze({ | ||
resolve: promiseResolve, | ||
reject: promiseReject, | ||
progress: promiseProgress | ||
}) | ||
}; | ||
deferred.promise = freeze(promise); | ||
listeners = []; | ||
progressHandlers = []; | ||
/** | ||
* Resolves this {@link Deferred}'s {@link Promise} with val as the | ||
* resolution value. | ||
* @memberOf Resolver | ||
* @param val {*|Promise} If val is anything but a Promise, resolves this | ||
* Deferred's Promise with val. If val is a Promise, puts this Deferred's | ||
* Promise into the same state as val. For example, if val is a rejected | ||
* promise, this Deferred will become rejected. | ||
* @return {Promise} a promise for the resolution value | ||
*/ | ||
resolver.resolve = deferred.resolve = function promiseResolve(val) { | ||
return _resolve(val); | ||
}; | ||
/** | ||
* Rejects this {@link Deferred}'s {@link Promise} with err as the | ||
* reason. | ||
* @memberOf Resolver | ||
* @param err anything | ||
* @return {Promise} a promise for the rejection value | ||
*/ | ||
resolver.reject = deferred.reject = function promiseReject(err) { | ||
return _resolve(rejected(err)); | ||
}; | ||
/** | ||
* Emits a progress update to all progress observers registered with | ||
* this {@link Deferred}'s {@link Promise} | ||
* @memberOf Resolver | ||
* @param update anything | ||
*/ | ||
resolver.progress = deferred.progress = function promiseProgress(update) { | ||
_progress(update); | ||
}; | ||
deferred.resolver = freeze(resolver); | ||
/** | ||
* Pre-resolution then() that adds the supplied callback, errback, and progback | ||
@@ -399,2 +344,36 @@ * functions to the registered listeners | ||
return deferred; | ||
/** | ||
* Wrapper to allow _then to be replaced safely | ||
* @param [callback] {Function} resolution handler | ||
* @param [errback] {Function} rejection handler | ||
* @param [progback] {Function} progress handler | ||
* @return {Promise} new Promise | ||
* @throws {Error} if any argument is not null, undefined, or a Function | ||
*/ | ||
function then(callback, errback, progback) { | ||
return _then(callback, errback, progback); | ||
} | ||
/** | ||
* Wrapper to allow _resolve to be replaced | ||
*/ | ||
function promiseResolve(val) { | ||
return _resolve(val); | ||
} | ||
/** | ||
* Wrapper to allow _resolve to be replaced | ||
*/ | ||
function promiseReject(err) { | ||
return _resolve(rejected(err)); | ||
} | ||
/** | ||
* Wrapper to allow _progress to be replaced | ||
* @param {*} update progress update | ||
*/ | ||
function promiseProgress(update) { | ||
_progress(update); | ||
} | ||
} | ||
@@ -672,2 +651,4 @@ | ||
function(reduceFunc /*, initialValue */) { | ||
/*jshint maxcomplexity: 7*/ | ||
// ES5 dictates that reduce.length === 1 | ||
@@ -684,3 +665,2 @@ | ||
// See https://github.com/jshint/jshint/issues/392 | ||
/*jshint newcap: false */ | ||
arr = Object(this); | ||
@@ -687,0 +667,0 @@ len = arr.length >>> 0; |
Sorry, the diff of this file is not supported yet
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
96
47356
1238