Commit be74db04 authored by Romain Courteaud's avatar Romain Courteaud

Add RSVP#any

parent 34045c37
...@@ -2,7 +2,7 @@ import { EventTarget } from "./rsvp/events"; ...@@ -2,7 +2,7 @@ import { EventTarget } from "./rsvp/events";
import { CancellationError } from "./rsvp/cancellation_error"; import { CancellationError } from "./rsvp/cancellation_error";
import { Promise } from "./rsvp/promise"; import { Promise } from "./rsvp/promise";
import { denodeify } from "./rsvp/node"; import { denodeify } from "./rsvp/node";
import { all } from "./rsvp/all"; import { all, any } from "./rsvp/all";
import { delay, timeout } from "./rsvp/timeout"; import { delay, timeout } from "./rsvp/timeout";
import { hash } from "./rsvp/hash"; import { hash } from "./rsvp/hash";
import { rethrow } from "./rsvp/rethrow"; import { rethrow } from "./rsvp/rethrow";
...@@ -15,4 +15,4 @@ function configure(name, value) { ...@@ -15,4 +15,4 @@ function configure(name, value) {
config[name] = value; config[name] = value;
} }
export { CancellationError, Promise, EventTarget, all, delay, timeout, hash, rethrow, defer, denodeify, configure, resolve, reject }; export { CancellationError, Promise, EventTarget, all, any, delay, timeout, hash, rethrow, defer, denodeify, configure, resolve, reject };
...@@ -2,18 +2,33 @@ ...@@ -2,18 +2,33 @@
import { Promise } from "./promise"; import { Promise } from "./promise";
function all(promises) { function promiseAtLeast(expected_count, promises) {
if (Object.prototype.toString.call(promises) !== "[object Array]") { if (Object.prototype.toString.call(promises) !== "[object Array]") {
throw new TypeError('You must pass an array to all.'); throw new TypeError('You must pass an array to all.');
} }
function canceller() {
var promise;
for (var i = 0; i < promises.length; i++) {
promise = promises[i];
if (promise && typeof promise.then === 'function') {
promise.cancel();
}
}
}
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
var results = [], remaining = promises.length, var results = [], remaining = promises.length,
promise; promise, remaining_count = promises.length - expected_count;
if (remaining === 0) { if (remaining === 0) {
if (expected_count === 1) {
resolve();
} else {
resolve([]); resolve([]);
} }
}
function resolver(index) { function resolver(index) {
return function(value) { return function(value) {
...@@ -23,8 +38,13 @@ function all(promises) { ...@@ -23,8 +38,13 @@ function all(promises) {
function resolveAll(index, value) { function resolveAll(index, value) {
results[index] = value; results[index] = value;
if (--remaining === 0) { if (--remaining === remaining_count) {
if (remaining_count === 0) {
resolve(results); resolve(results);
} else {
resolve(value);
canceller();
}
} }
} }
...@@ -37,16 +57,16 @@ function all(promises) { ...@@ -37,16 +57,16 @@ function all(promises) {
resolveAll(i, promise); resolveAll(i, promise);
} }
} }
}, function() { }, canceller
var promise; );
for (var i = 0; i < promises.length; i++) { }
promise = promises[i];
if (promise && typeof promise.then === 'function') { function all(promises) {
promise.cancel(); return promiseAtLeast(promises.length, promises);
} }
}
}); function any(promises) {
return promiseAtLeast(1, promises);
} }
export { all }; export { all, any };
...@@ -725,6 +725,179 @@ describe("RSVP extensions", function() { ...@@ -725,6 +725,179 @@ describe("RSVP extensions", function() {
}); });
}); });
describe("RSVP.any", function() {
it('should exist', function() {
assert(RSVP.any);
});
it('throws when not passed an array', function() {
assert.throws(function () {
var any = RSVP.any();
}, TypeError);
assert.throws(function () {
var any = RSVP.any('');
}, TypeError);
assert.throws(function () {
var any = RSVP.any({});
}, TypeError);
});
specify('fulfilled only after one of the other promises are fulfilled', function(done) {
var firstResolved, secondResolved, firstResolver, secondResolver;
var first = new RSVP.Promise(function(resolve) {
firstResolver = resolve;
});
first.then(function() {
firstResolved = true;
});
var second = new RSVP.Promise(function(resolve) {
secondResolver = resolve;
});
second.then(function() {
secondResolved = true;
});
setTimeout(function() {
firstResolver(true);
}, 20);
setTimeout(function() {
secondResolver(true);
}, 0);
RSVP.any([first, second]).then(function() {
assert(firstResolved === undefined);
assert(secondResolved);
done();
});
});
specify('rejected as soon as a promise is rejected', function(done) {
var firstResolver, secondResolver;
var first = new RSVP.Promise(function(resolve, reject) {
firstResolver = { resolve: resolve, reject: reject };
});
var second = new RSVP.Promise(function(resolve, reject) {
secondResolver = { resolve: resolve, reject: reject };
});
setTimeout(function() {
firstResolver.reject({});
}, 0);
setTimeout(function() {
secondResolver.resolve(true);
}, 5000);
RSVP.any([first, second]).then(function() {
assert(false);
}, function() {
assert(first.isRejected);
assert(!second.isResolved);
done();
});
});
specify('passes the resolved value of the fulfilled promise to the callback', function(done) {
var firstResolver, secondResolver, thirdResolver;
var first = new RSVP.Promise(function(resolve, reject) {
firstResolver = { resolve: resolve, reject: reject };
});
var second = new RSVP.Promise(function(resolve, reject) {
secondResolver = { resolve: resolve, reject: reject };
});
var third = new RSVP.Promise(function(resolve, reject) {
thirdResolver = { resolve: resolve, reject: reject };
});
thirdResolver.resolve(3);
firstResolver.resolve(1);
secondResolver.resolve(2);
RSVP.any([first, second, third]).then(function(result) {
assert.equal(result, 3);
done();
});
});
specify('resolves an empty array passed to RSVP.any()', function(done) {
RSVP.any([]).then(function(result) {
console.log(result);
assert.equal(result, undefined);
done();
});
});
specify('works with a mix of promises and thenables and non-promises', function(done) {
var promise = new RSVP.Promise(function(resolve) { resolve(1); });
var syncThenable = { then: function (onFulfilled) { onFulfilled(2); } };
var asyncThenable = { then: function (onFulfilled) { setTimeout(function() { onFulfilled(3); }, 0); } };
var nonPromise = 4;
RSVP.any([promise, syncThenable, asyncThenable, nonPromise]).then(function(result) {
assert.equal(result, 2);
done();
});
});
specify('cancel the remaining promises as soon as resolved', function(done) {
var promise = new RSVP.Promise(function(resolve) { resolve(1); });
var asyncPromise = new RSVP.Promise(function(resolve) {
setTimeout(function() { resolve(2); }, 0); });
RSVP.any([promise, asyncPromise]).then(function(result) {
assert.equal(result, 1);
});
setTimeout(function() {
assert(asyncPromise.isRejected);
assert(asyncPromise.rejectedReason instanceof RSVP.CancellationError);
done();
}, 20);
});
specify('cancel the array of promise as soon as cancelled', function(done) {
var firstResolver, secondResolver;
var first = new RSVP.Promise(function(resolve, reject) {
firstResolver = { resolve: resolve, reject: reject };
});
var second = new RSVP.Promise(function(resolve, reject) {
secondResolver = { resolve: resolve, reject: reject };
});
setTimeout(function() {
firstResolver.reject({});
}, 0);
setTimeout(function() {
secondResolver.resolve(true);
}, 5000);
var all_promise = RSVP.any([first, second]);
all_promise.cancel();
setTimeout(function() {
assert(first.isRejected);
assert(first.rejectedReason instanceof RSVP.CancellationError);
assert(second.isRejected);
assert(second.rejectedReason instanceof RSVP.CancellationError);
done();
}, 20);
});
});
describe("RSVP.reject", function(){ describe("RSVP.reject", function(){
specify("it should exist", function(){ specify("it should exist", function(){
assert(RSVP.reject); assert(RSVP.reject);
......
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