Commit 33b00c23 authored by Yehuda Katz's avatar Yehuda Katz

Merge branch 'promise-constructor' of https://github.com/domenic/rsvp.js into...

Merge branch 'promise-constructor' of https://github.com/domenic/rsvp.js into domenic-promise-constructor
parents 42c0384f f5fb25b2
...@@ -13,6 +13,14 @@ define( ...@@ -13,6 +13,14 @@ define(
var Promise = function(resolver) { var Promise = function(resolver) {
var promise = this; var promise = this;
if (typeof resolver !== 'function') {
throw new TypeError('You must pass a resolver function as the sole argument to the promise constructor');
}
if (!(promise instanceof Promise)) {
return new Promise(resolver);
}
var resolvePromise = function(value) { var resolvePromise = function(value) {
resolve(promise, value); resolve(promise, value);
resolvePromise = noop; resolvePromise = noop;
...@@ -33,9 +41,7 @@ define( ...@@ -33,9 +41,7 @@ define(
this.trigger('error', { detail: event.detail }); this.trigger('error', { detail: event.detail });
}, this); }, this);
if (resolver) {
resolver(resolvePromise, rejectPromise); resolver(resolvePromise, rejectPromise);
}
}; };
var invokeCallback = function(type, promise, callback, event) { var invokeCallback = function(type, promise, callback, event) {
...@@ -73,8 +79,10 @@ define( ...@@ -73,8 +79,10 @@ define(
}; };
Promise.prototype = { Promise.prototype = {
constructor: Promise,
then: function(done, fail) { then: function(done, fail) {
var thenPromise = new Promise(); var thenPromise = new Promise(function() {});
if (this.isFulfilled) { if (this.isFulfilled) {
config.async(function() { config.async(function() {
...@@ -101,6 +109,18 @@ define( ...@@ -101,6 +109,18 @@ define(
}; };
function resolve(promise, value) { function resolve(promise, value) {
if (value && typeof value.then === 'function') {
value.then(function(val) {
resolve(promise, val);
}, function(val) {
reject(promise, val);
});
} else {
fulfill(promise, value);
}
}
function fulfill(promise, value) {
config.async(function() { config.async(function() {
promise.trigger('promise:resolved', { detail: value }); promise.trigger('promise:resolved', { detail: value });
promise.isFulfilled = true; promise.isFulfilled = true;
...@@ -118,7 +138,7 @@ define( ...@@ -118,7 +138,7 @@ define(
function all(promises) { function all(promises) {
var i, results = []; var i, results = [];
var allPromise = new Promise(); var allPromise = new Promise(function() {});
var remaining = promises.length; var remaining = promises.length;
if (remaining === 0) { if (remaining === 0) {
......
...@@ -200,6 +200,14 @@ define("rsvp", ...@@ -200,6 +200,14 @@ define("rsvp",
var Promise = function(resolver) { var Promise = function(resolver) {
var promise = this; var promise = this;
if (typeof resolver !== 'function') {
throw new TypeError('You must pass a resolver function as the sole argument to the promise constructor');
}
if (!(promise instanceof Promise)) {
return new Promise(resolver);
}
var resolvePromise = function(value) { var resolvePromise = function(value) {
resolve(promise, value); resolve(promise, value);
resolvePromise = noop; resolvePromise = noop;
...@@ -220,9 +228,7 @@ define("rsvp", ...@@ -220,9 +228,7 @@ define("rsvp",
this.trigger('error', { detail: event.detail }); this.trigger('error', { detail: event.detail });
}, this); }, this);
if (resolver) {
resolver(resolvePromise, rejectPromise); resolver(resolvePromise, rejectPromise);
}
}; };
var invokeCallback = function(type, promise, callback, event) { var invokeCallback = function(type, promise, callback, event) {
...@@ -260,8 +266,10 @@ define("rsvp", ...@@ -260,8 +266,10 @@ define("rsvp",
}; };
Promise.prototype = { Promise.prototype = {
constructor: Promise,
then: function(done, fail) { then: function(done, fail) {
var thenPromise = new Promise(); var thenPromise = new Promise(function() {});
if (this.isFulfilled) { if (this.isFulfilled) {
config.async(function() { config.async(function() {
...@@ -288,6 +296,18 @@ define("rsvp", ...@@ -288,6 +296,18 @@ define("rsvp",
}; };
function resolve(promise, value) { function resolve(promise, value) {
if (value && typeof value.then === 'function') {
value.then(function(val) {
resolve(promise, val);
}, function(val) {
reject(promise, val);
});
} else {
fulfill(promise, value);
}
}
function fulfill(promise, value) {
config.async(function() { config.async(function() {
promise.trigger('promise:resolved', { detail: value }); promise.trigger('promise:resolved', { detail: value });
promise.isFulfilled = true; promise.isFulfilled = true;
...@@ -305,7 +325,7 @@ define("rsvp", ...@@ -305,7 +325,7 @@ define("rsvp",
function all(promises) { function all(promises) {
var i, results = []; var i, results = [];
var allPromise = new Promise(); var allPromise = new Promise(function() {});
var remaining = promises.length; var remaining = promises.length;
if (remaining === 0) { if (remaining === 0) {
......
(function(){var a,b;(function(){var c={},d={};a=function(a,b,d){c[a]={deps:b,callback:d}},b=function(a){if(d[a])return d[a];d[a]={};var e=c[a],f=e.deps,g=e.callback,h=[],i;for(var j=0,k=f.length;j<k;j++)f[j]==="exports"?h.push(i={}):h.push(b(f[j]));var l=g.apply(this,h);return d[a]=i||l}})(),a("rsvp/async",["exports"],function(a){"use strict";var b=typeof window!="undefined"?window:{},c=b.MutationObserver||b.WebKitMutationObserver,d;if(typeof process!="undefined"&&{}.toString.call(process)==="[object process]")d=function(a,b){process.nextTick(function(){a.call(b)})};else if(c){var e=[],f=new c(function(){var a=e.slice();e=[],a.forEach(function(a){var b=a[0],c=a[1];b.call(c)})}),g=document.createElement("div");f.observe(g,{attributes:!0}),window.addEventListener("unload",function(){f.disconnect(),f=null}),d=function(a,b){e.push([a,b]),g.setAttribute("drainQueue","drainQueue")}}else d=function(a,b){setTimeout(function(){a.call(b)},1)};a.async=d}),a("rsvp/events",["exports"],function(a){"use strict";var b=function(a,b){this.type=a;for(var c in b){if(!b.hasOwnProperty(c))continue;this[c]=b[c]}},c=function(a,b){for(var c=0,d=a.length;c<d;c++)if(a[c][0]===b)return c;return-1},d=function(a){var b=a._promiseCallbacks;b||(b=a._promiseCallbacks={});return b},e={mixin:function(a){a.on=this.on,a.off=this.off,a.trigger=this.trigger;return a},on:function(a,b,e){var f=d(this),g,h;a=a.split(/\s+/),e=e||this;while(h=a.shift())g=f[h],g||(g=f[h]=[]),c(g,b)===-1&&g.push([b,e])},off:function(a,b){var e=d(this),f,g,h;a=a.split(/\s+/);while(g=a.shift()){if(!b){e[g]=[];continue}f=e[g],h=c(f,b),h!==-1&&f.splice(h,1)}},trigger:function(a,c){var e=d(this),f,g,h,i,j;if(f=e[a])for(var k=0;k<f.length;k++)g=f[k],h=g[0],i=g[1],typeof c!="object"&&(c={detail:c}),j=new b(a,c),h.call(i,j)}};a.EventTarget=e}),a("rsvp",["rsvp/async","rsvp/events","exports"],function(a,b,c){function n(){var a={},b=new h(function(b,c){a.resolve=b,a.reject=c});a.promise=b;return a}function m(a,b){f[a]=b}function l(a){var b,c=[],d=new h,e=a.length;e===0&&j(d,[]);var f=function(a){return function(b){g(a,b)}},g=function(a,b){c[a]=b,--e===0&&j(d,c)},i=function(a){k(d,a)};for(b=0;b<a.length;b++)a[b]&&typeof a[b].then=="function"?a[b].then(f(b),i):g(b,a[b]);return d}function k(a,b){f.async(function(){a.trigger("promise:failed",{detail:b}),a.isRejected=!0,a.rejectedReason=b})}function j(a,b){f.async(function(){a.trigger("promise:resolved",{detail:b}),a.isFulfilled=!0,a.fulfillmentValue=b})}"use strict";var d=a.async,e=b.EventTarget,f={};f.async=d;var g=function(){},h=function(a){var b=this,c=function(a){j(b,a),c=g,d=g},d=function(a){k(b,a),c=g,d=g};this.on("promise:resolved",function(a){this.trigger("success",{detail:a.detail})},this),this.on("promise:failed",function(a){this.trigger("error",{detail:a.detail})},this),a&&a(c,d)},i=function(a,b,c,d){var e=typeof c=="function",f,g,h,i;if(e)try{f=c(d.detail),h=!0}catch(l){i=!0,g=l}else f=d.detail,h=!0;f&&typeof f.then=="function"?f.then(function(a){j(b,a)},function(a){k(b,a)}):e&&h?j(b,f):i?k(b,g):a==="resolve"?j(b,f):a==="reject"&&k(b,f)};h.prototype={then:function(a,b){var c=new h;this.isFulfilled&&f.async(function(){i("resolve",c,a,{detail:this.fulfillmentValue})},this),this.isRejected&&f.async(function(){i("reject",c,b,{detail:this.rejectedReason})},this),this.on("promise:resolved",function(b){i("resolve",c,a,b)}),this.on("promise:failed",function(a){i("reject",c,b,a)});return c}},e.mixin(h.prototype),c.Promise=h,c.all=l,c.defer=n,c.configure=m}),window.RSVP=b("rsvp")})() (function(){var a,b;(function(){var c={},d={};a=function(a,b,d){c[a]={deps:b,callback:d}},b=function(a){if(d[a])return d[a];d[a]={};var e=c[a],f=e.deps,g=e.callback,h=[],i;for(var j=0,k=f.length;j<k;j++)f[j]==="exports"?h.push(i={}):h.push(b(f[j]));var l=g.apply(this,h);return d[a]=i||l}})(),a("rsvp/async",["exports"],function(a){"use strict";var b=typeof window!="undefined"?window:{},c=b.MutationObserver||b.WebKitMutationObserver,d;if(typeof process!="undefined"&&{}.toString.call(process)==="[object process]")d=function(a,b){process.nextTick(function(){a.call(b)})};else if(c){var e=[],f=new c(function(){var a=e.slice();e=[],a.forEach(function(a){var b=a[0],c=a[1];b.call(c)})}),g=document.createElement("div");f.observe(g,{attributes:!0}),window.addEventListener("unload",function(){f.disconnect(),f=null}),d=function(a,b){e.push([a,b]),g.setAttribute("drainQueue","drainQueue")}}else d=function(a,b){setTimeout(function(){a.call(b)},1)};a.async=d}),a("rsvp/events",["exports"],function(a){"use strict";var b=function(a,b){this.type=a;for(var c in b){if(!b.hasOwnProperty(c))continue;this[c]=b[c]}},c=function(a,b){for(var c=0,d=a.length;c<d;c++)if(a[c][0]===b)return c;return-1},d=function(a){var b=a._promiseCallbacks;b||(b=a._promiseCallbacks={});return b},e={mixin:function(a){a.on=this.on,a.off=this.off,a.trigger=this.trigger;return a},on:function(a,b,e){var f=d(this),g,h;a=a.split(/\s+/),e=e||this;while(h=a.shift())g=f[h],g||(g=f[h]=[]),c(g,b)===-1&&g.push([b,e])},off:function(a,b){var e=d(this),f,g,h;a=a.split(/\s+/);while(g=a.shift()){if(!b){e[g]=[];continue}f=e[g],h=c(f,b),h!==-1&&f.splice(h,1)}},trigger:function(a,c){var e=d(this),f,g,h,i,j;if(f=e[a])for(var k=0;k<f.length;k++)g=f[k],h=g[0],i=g[1],typeof c!="object"&&(c={detail:c}),j=new b(a,c),h.call(i,j)}};a.EventTarget=e}),a("rsvp",["rsvp/async","rsvp/events","exports"],function(a,b,c){function o(){var a={},b=new h(function(b,c){a.resolve=b,a.reject=c});a.promise=b;return a}function n(a,b){f[a]=b}function m(a){var b,c=[],d=new h(function(){}),e=a.length;e===0&&j(d,[]);var f=function(a){return function(b){g(a,b)}},g=function(a,b){c[a]=b,--e===0&&j(d,c)},i=function(a){l(d,a)};for(b=0;b<a.length;b++)a[b]&&typeof a[b].then=="function"?a[b].then(f(b),i):g(b,a[b]);return d}function l(a,b){f.async(function(){a.trigger("promise:failed",{detail:b}),a.isRejected=!0,a.rejectedReason=b})}function k(a,b){f.async(function(){a.trigger("promise:resolved",{detail:b}),a.isFulfilled=!0,a.fulfillmentValue=b})}function j(a,b){b&&typeof b.then=="function"?b.then(function(b){j(a,b)},function(b){l(a,b)}):k(a,b)}"use strict";var d=a.async,e=b.EventTarget,f={};f.async=d;var g=function(){},h=function(a){var b=this;if(typeof a!="function")throw new TypeError("You must pass a resolver function as the sole argument to the promise constructor");if(!(b instanceof h))return new h(a);var c=function(a){j(b,a),c=g,d=g},d=function(a){l(b,a),c=g,d=g};this.on("promise:resolved",function(a){this.trigger("success",{detail:a.detail})},this),this.on("promise:failed",function(a){this.trigger("error",{detail:a.detail})},this),a(c,d)},i=function(a,b,c,d){var e=typeof c=="function",f,g,h,i;if(e)try{f=c(d.detail),h=!0}catch(k){i=!0,g=k}else f=d.detail,h=!0;f&&typeof f.then=="function"?f.then(function(a){j(b,a)},function(a){l(b,a)}):e&&h?j(b,f):i?l(b,g):a==="resolve"?j(b,f):a==="reject"&&l(b,f)};h.prototype={constructor:h,then:function(a,b){var c=new h(function(){});this.isFulfilled&&f.async(function(){i("resolve",c,a,{detail:this.fulfillmentValue})},this),this.isRejected&&f.async(function(){i("reject",c,b,{detail:this.rejectedReason})},this),this.on("promise:resolved",function(b){i("resolve",c,a,b)}),this.on("promise:failed",function(a){i("reject",c,b,a)});return c}},e.mixin(h.prototype),c.Promise=h,c.all=m,c.defer=o,c.configure=n}),window.RSVP=b("rsvp")})()
...@@ -9,6 +9,14 @@ var noop = function() {}; ...@@ -9,6 +9,14 @@ var noop = function() {};
var Promise = function(resolver) { var Promise = function(resolver) {
var promise = this; var promise = this;
if (typeof resolver !== 'function') {
throw new TypeError('You must pass a resolver function as the sole argument to the promise constructor');
}
if (!(promise instanceof Promise)) {
return new Promise(resolver);
}
var resolvePromise = function(value) { var resolvePromise = function(value) {
resolve(promise, value); resolve(promise, value);
resolvePromise = noop; resolvePromise = noop;
...@@ -29,9 +37,7 @@ var Promise = function(resolver) { ...@@ -29,9 +37,7 @@ var Promise = function(resolver) {
this.trigger('error', { detail: event.detail }); this.trigger('error', { detail: event.detail });
}, this); }, this);
if (resolver) {
resolver(resolvePromise, rejectPromise); resolver(resolvePromise, rejectPromise);
}
}; };
var invokeCallback = function(type, promise, callback, event) { var invokeCallback = function(type, promise, callback, event) {
...@@ -69,8 +75,10 @@ var invokeCallback = function(type, promise, callback, event) { ...@@ -69,8 +75,10 @@ var invokeCallback = function(type, promise, callback, event) {
}; };
Promise.prototype = { Promise.prototype = {
constructor: Promise,
then: function(done, fail) { then: function(done, fail) {
var thenPromise = new Promise(); var thenPromise = new Promise(function() {});
if (this.isFulfilled) { if (this.isFulfilled) {
config.async(function() { config.async(function() {
...@@ -97,6 +105,18 @@ Promise.prototype = { ...@@ -97,6 +105,18 @@ Promise.prototype = {
}; };
function resolve(promise, value) { function resolve(promise, value) {
if (value && typeof value.then === 'function') {
value.then(function(val) {
resolve(promise, val);
}, function(val) {
reject(promise, val);
});
} else {
fulfill(promise, value);
}
}
function fulfill(promise, value) {
config.async(function() { config.async(function() {
promise.trigger('promise:resolved', { detail: value }); promise.trigger('promise:resolved', { detail: value });
promise.isFulfilled = true; promise.isFulfilled = true;
...@@ -114,7 +134,7 @@ function reject(promise, value) { ...@@ -114,7 +134,7 @@ function reject(promise, value) {
function all(promises) { function all(promises) {
var i, results = []; var i, results = [];
var allPromise = new Promise(); var allPromise = new Promise(function() {});
var remaining = promises.length; var remaining = promises.length;
if (remaining === 0) { if (remaining === 0) {
......
/*global RSVP, describe, specify, assert */ /*global RSVP, describe, specify, it, assert */
describe("RSVP extensions", function() { describe("RSVP extensions", function() {
describe("Promise constructor", function() {
it('should exist and have length 1', function() {
assert(RSVP.Promise);
assert.equal(RSVP.Promise.length, 1);
});
it('should fulfill if `resolve` is called with a value', function(done) {
var promise = new RSVP.Promise(function(resolve) { resolve('value'); });
promise.then(function(value) {
assert.equal(value, 'value');
done();
});
});
it('should reject if `reject` is called with a reason', function(done) {
var promise = new RSVP.Promise(function(resolve, reject) { reject('reason'); });
promise.then(function() {
assert(false);
done();
}, function(reason) {
assert.equal(reason, 'reason');
done();
});
});
it('should be a constructor', function() {
var promise = new RSVP.Promise(function() {});
assert.equal(Object.getPrototypeOf(promise), RSVP.Promise.prototype, '[[Prototype]] equals Promise.prototype');
assert.equal(promise.constructor, RSVP.Promise, 'constructor property of instances is set correctly');
assert.equal(RSVP.Promise.prototype.constructor, RSVP.Promise, 'constructor property of prototype is set correctly');
});
it('should work without `new`', function(done) {
var promise = RSVP.Promise(function(resolve) { resolve('value'); });
promise.then(function(value) {
assert.equal(value, 'value');
done();
});
});
it('should throw a `TypeError` if not given a function', function() {
assert.throws(function () {
var promise = new RSVP.Promise();
}, TypeError);
assert.throws(function () {
var promise = new RSVP.Promise({});
}, TypeError);
assert.throws(function () {
var promise = new RSVP.Promise('boo!');
}, TypeError);
});
describe('assimilation', function() {
it('should assimilate if `resolve` is called with a fulfilled promise', function(done) {
var originalPromise = new RSVP.Promise(function(resolve) { resolve('original value'); });
var promise = new RSVP.Promise(function(resolve) { resolve(originalPromise); });
promise.then(function(value) {
assert.equal(value, 'original value');
done();
});
});
it('should assimilate if `resolve` is called with a rejected promise', function(done) {
var originalPromise = new RSVP.Promise(function(resolve, reject) { reject('original reason'); });
var promise = new RSVP.Promise(function(resolve) { resolve(originalPromise); });
promise.then(function() {
assert(false);
done();
}, function(reason) {
assert.equal(reason, 'original reason');
done();
});
});
it('should assimilate if `resolve` is called with a fulfilled thenable', function(done) {
var originalThenable = {
then: function (onFulfilled) {
setTimeout(function() { onFulfilled('original value'); }, 0);
}
};
var promise = new RSVP.Promise(function(resolve) { resolve(originalThenable); });
promise.then(function(value) {
assert.equal(value, 'original value');
done();
});
});
it('should assimilate if `resolve` is called with a rejected thenable', function(done) {
var originalThenable = {
then: function (onFulfilled, onRejected) {
setTimeout(function() { onRejected('original reason'); }, 0);
}
};
var promise = new RSVP.Promise(function(resolve) { resolve(originalThenable); });
promise.then(function() {
assert(false);
done();
}, function(reason) {
assert.equal(reason, 'original reason');
done();
});
});
it('should assimilate two levels deep, for fulfillment', function(done) {
var originalPromise = new RSVP.Promise(function(resolve) { resolve('original value'); });
var nextPromise = new RSVP.Promise(function(resolve) { resolve(originalPromise); });
var promise = new RSVP.Promise(function(resolve) { resolve(nextPromise); });
promise.then(function(value) {
assert.equal(value, 'original value');
done();
});
});
it('should assimilate two levels deep, for rejection', function(done) {
var originalPromise = new RSVP.Promise(function(resolve, reject) { reject('original reason'); });
var nextPromise = new RSVP.Promise(function(resolve) { resolve(originalPromise); });
var promise = new RSVP.Promise(function(resolve) { resolve(nextPromise); });
promise.then(function() {
assert(false);
done();
}, function(reason) {
assert.equal(reason, 'original reason');
done();
});
});
it('should assimilate three levels deep, mixing thenables and promises (fulfilled case)', function(done) {
var originalPromise = new RSVP.Promise(function(resolve) { resolve('original value'); });
var intermediateThenable = {
then: function (onFulfilled) {
setTimeout(function() { onFulfilled(originalPromise); }, 0);
}
};
var promise = new RSVP.Promise(function(resolve) { resolve(intermediateThenable); });
promise.then(function(value) {
assert.equal(value, 'original value');
done();
});
});
it('should assimilate three levels deep, mixing thenables and promises (rejected case)', function(done) {
var originalPromise = new RSVP.Promise(function(resolve, reject) { reject('original reason'); });
var intermediateThenable = {
then: function (onFulfilled) {
setTimeout(function() { onFulfilled(originalPromise); }, 0);
}
};
var promise = new RSVP.Promise(function(resolve) { resolve(intermediateThenable); });
promise.then(function() {
assert(false);
done();
}, function(reason) {
assert.equal(reason, 'original reason');
done();
});
});
});
});
describe("RSVP.defer", function() { describe("RSVP.defer", function() {
specify("It should return a resolver and promise together", function(done) { specify("It should return a resolver and promise together", function(done) {
var deferred = RSVP.defer(), value = {}; var deferred = RSVP.defer(), value = {};
...@@ -27,7 +200,7 @@ describe("RSVP extensions", function() { ...@@ -27,7 +200,7 @@ describe("RSVP extensions", function() {
}); });
describe("RSVP.all", function() { describe("RSVP.all", function() {
specify('it should exist', function() { it('should exist', function() {
assert(RSVP.all); assert(RSVP.all);
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment