Commit 640708fc authored by Stefan Penner's avatar Stefan Penner

Merge pull request #92 from stefanpenner/fix_callback_assimilation

Fix callback assimilation
parents 47b35e23 dc6ce37a
...@@ -10,8 +10,7 @@ function isFunction(x){ ...@@ -10,8 +10,7 @@ function isFunction(x){
} }
var Promise = function(resolver) { var Promise = function(resolver) {
var promise = this, var promise = this;
resolved = false;
if (typeof resolver !== 'function') { if (typeof resolver !== 'function') {
throw new TypeError('You must pass a resolver function as the sole argument to the promise constructor'); throw new TypeError('You must pass a resolver function as the sole argument to the promise constructor');
...@@ -22,14 +21,10 @@ var Promise = function(resolver) { ...@@ -22,14 +21,10 @@ var Promise = function(resolver) {
} }
var resolvePromise = function(value) { var resolvePromise = function(value) {
if (resolved) { return; }
resolved = true;
resolve(promise, value); resolve(promise, value);
}; };
var rejectPromise = function(value) { var rejectPromise = function(value) {
if (resolved) { return; }
resolved = true;
reject(promise, value); reject(promise, value);
}; };
...@@ -88,7 +83,9 @@ var invokeCallback = function(type, promise, callback, event) { ...@@ -88,7 +83,9 @@ var invokeCallback = function(type, promise, callback, event) {
Promise.prototype = { Promise.prototype = {
constructor: Promise, constructor: Promise,
isFulfilled: undefined,
isRejected: undefined,
isResolved: undefined,
then: function(done, fail) { then: function(done, fail) {
this.off('error', onerror); this.off('error', onerror);
...@@ -158,6 +155,9 @@ function handleThenable(promise, value) { ...@@ -158,6 +155,9 @@ function handleThenable(promise, value) {
} }
function fulfill(promise, value) { function fulfill(promise, value) {
if (promise.isResolved){ return; }
promise.isResolved = true;
config.async(function() { config.async(function() {
promise.trigger('promise:resolved', { detail: value }); promise.trigger('promise:resolved', { detail: value });
promise.isFulfilled = true; promise.isFulfilled = true;
...@@ -166,6 +166,9 @@ function fulfill(promise, value) { ...@@ -166,6 +166,9 @@ function fulfill(promise, value) {
} }
function reject(promise, value) { function reject(promise, value) {
if (promise.isResolved){ return; }
promise.isResolved = true;
config.async(function() { config.async(function() {
promise.trigger('promise:failed', { detail: value }); promise.trigger('promise:failed', { detail: value });
promise.isRejected = true; promise.isRejected = true;
......
...@@ -5,33 +5,9 @@ function objectOrFunction(x) { ...@@ -5,33 +5,9 @@ function objectOrFunction(x) {
} }
function resolve(thenable) { function resolve(thenable) {
if (thenable instanceof Promise) { return new Promise(function(resolve, reject) {
return thenable;
}
var promise = new Promise(function(resolve, reject) {
var then;
try {
if ( objectOrFunction(thenable) ) {
then = thenable.then;
if (typeof then === "function") {
then.call(thenable, resolve, reject);
} else {
resolve(thenable); resolve(thenable);
}
} else {
resolve(thenable);
}
} catch(error) {
reject(error);
}
}); });
return promise;
} }
export { resolve }; export { resolve };
...@@ -90,6 +90,42 @@ describe("RSVP extensions", function() { ...@@ -90,6 +90,42 @@ describe("RSVP extensions", function() {
}); });
}); });
it('should not resolve multiple times', function(done) {
var resolver, rejector, fulfilled = 0, rejected = 0;
var thenable = {
then: function(resolve, reject) {
resolver = resolve;
rejector = reject;
}
};
var promise = RSVP.Promise(function(resolve) {
resolve(1);
});
promise.then(function(value){
return thenable;
}).then(function(value){
fulfilled++;
}, function(reason) {
rejected++;
});
setTimeout(function() {
resolver(1);
resolver(1);
rejector(1);
rejector(1);
setTimeout(function() {
assert.equal(fulfilled, 1);
assert.equal(rejected, 0);
done();
}, 20);
}, 20);
});
describe('assimilation', function() { describe('assimilation', function() {
it('should assimilate if `resolve` is called with a fulfilled promise', function(done) { it('should assimilate if `resolve` is called with a fulfilled promise', function(done) {
var originalPromise = new RSVP.Promise(function(resolve) { resolve('original value'); }); var originalPromise = new RSVP.Promise(function(resolve) { resolve('original value'); });
...@@ -665,12 +701,6 @@ describe("RSVP extensions", function() { ...@@ -665,12 +701,6 @@ describe("RSVP extensions", function() {
assert(RSVP.resolve); assert(RSVP.resolve);
}); });
specify("it short circuits if RSVP.promise", function(){
var deferred = new RSVP.defer();
assert.equal(RSVP.resolve(deferred.promise), deferred.promise);
});
describe("1. If x is a promise, adopt its state ", function(){ describe("1. If x is a promise, adopt its state ", function(){
specify("1.1 If x is pending, promise must remain pending until x is fulfilled or rejected.", function(done){ specify("1.1 If x is pending, promise must remain pending until x is fulfilled or rejected.", function(done){
var expectedValue, resolver, thenable, wrapped; var expectedValue, resolver, thenable, wrapped;
...@@ -948,3 +978,57 @@ describe("RSVP extensions", function() { ...@@ -948,3 +978,57 @@ describe("RSVP extensions", function() {
}); });
}); });
}); });
// thanks to @wizardwerdna for the test case -> https://github.com/tildeio/rsvp.js/issues/66
// Only run these tests in node (phantomjs cannot handle them)
if (typeof module !== 'undefined' && module.exports) {
describe("using reduce to sum integers using promises", function(){
var resolve = RSVP.resolve;
it("should build the promise pipeline without error", function(){
var array, iters, pZero, i;
array = [];
iters = 1000;
for (i=1; i<=iters; i++) {
array.push(i);
}
pZero = resolve(0);
array.reduce(function(promise, nextVal) {
return promise.then(function(currentVal) {
return resolve(currentVal + nextVal);
});
}, pZero);
});
it("should get correct answer without blowing the nextTick stack", function(done){
var pZero, array, iters, result, i;
pZero = resolve(0);
array = [];
iters = 1000;
for (i=1; i<=iters; i++) {
array.push(i);
}
result = array.reduce(function(promise, nextVal) {
return promise.then(function(currentVal) {
return resolve(currentVal + nextVal);
});
}, pZero);
result.then(function(value){
assert.equal(value, (iters*(iters+1)/2));
done();
});
});
});
}
// thanks to @wizardwerdna for the test case -> https://github.com/tildeio/rsvp.js/issues/66
describe("using reduce to sum integers using promises", function(){
var resolve = RSVP.resolve;
it("should build the promise pipeline without error", function(){
var array, iters, pZero, i;
array = [];
iters = 1000;
for (i=1; i<=iters; i++) {
array.push(i);
}
pZero = resolve(0);
array.reduce(function(promise, nextVal) {
return promise.then(function(currentVal) {
return resolve(currentVal + nextVal);
});
}, pZero);
});
it("should get correct answer without blowing the nextTick stack", function(done){
var pZero, array, iters, result, i;
pZero = resolve(0);
array = [];
iters = 1000;
for (i=1; i<=iters; i++) {
array.push(i);
}
result = array.reduce(function(promise, nextVal) {
return promise.then(function(currentVal) {
return resolve(currentVal + nextVal);
});
}, pZero);
result.then(function(value){
assert.equal(value, (iters*(iters+1)/2));
done();
});
});
});
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