-
Notifications
You must be signed in to change notification settings - Fork 27.4k
feat($q): implement $q.race #14757
feat($q): implement $q.race #14757
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -636,6 +636,30 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) { | |
return deferred.promise; | ||
} | ||
|
||
/** | ||
* @ngdoc method | ||
* @name $q#race | ||
* @kind function | ||
* | ||
* @description | ||
* Returns a promise that is resolved similarly as the first promise to resolve. | ||
* | ||
* @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises. | ||
* @returns {Promise} Returns a promise that will be resolved as soon as the first promise in | ||
* `promises` is resolved. The resolved value will be the same as the value of the first | ||
* resolved promise. | ||
*/ | ||
|
||
function race(promises) { | ||
var deferred = defer(); | ||
|
||
forEach(promises, function(promise) { | ||
when(promise).then(deferred.resolve, deferred.reject); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @gkalpak Does this look good to you? (Notice I changed the initialization of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't realize you were using |
||
}); | ||
|
||
return deferred.promise; | ||
} | ||
|
||
var $Q = function Q(resolver) { | ||
if (!isFunction(resolver)) { | ||
throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver); | ||
|
@@ -665,6 +689,7 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) { | |
$Q.when = when; | ||
$Q.resolve = resolve; | ||
$Q.all = all; | ||
$Q.race = race; | ||
|
||
return $Q; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1986,6 +1986,78 @@ describe('q', function() { | |
}); | ||
}); | ||
|
||
describe('race (array)', function() { | ||
it('should do nothing if given an empty array', function() { | ||
q.race([]).then(success(), error()); | ||
expect(mockNextTick.queue.length).toBe(0); | ||
expect(logStr()).toBe(''); | ||
}); | ||
|
||
it('should resolve as soon as the first promise is settled by resolution', function() { | ||
var deferred1 = defer(), | ||
deferred2 = defer(); | ||
|
||
q.race([promise, deferred1.promise, deferred2.promise]).then(success(), error()); | ||
expect(logStr()).toBe(''); | ||
syncResolve(deferred1, 'hi'); | ||
expect(logStr()).toBe('success(hi)->hi'); | ||
syncResolve(deferred2, 'cau'); | ||
expect(logStr()).toBe('success(hi)->hi'); | ||
syncReject(deferred, 'hola'); | ||
expect(logStr()).toBe('success(hi)->hi'); | ||
}); | ||
|
||
it('should reject as soon as the first promise is settled by rejection', function() { | ||
var deferred1 = defer(), | ||
deferred2 = defer(); | ||
|
||
q.race([promise, deferred1.promise, deferred2.promise]).then(success(), error()); | ||
expect(logStr()).toBe(''); | ||
syncReject(deferred1, 'hi'); | ||
expect(logStr()).toBe('error(hi)->reject(hi)'); | ||
syncResolve(deferred2, 'cau'); | ||
expect(logStr()).toBe('error(hi)->reject(hi)'); | ||
syncReject(deferred, 'hola'); | ||
expect(logStr()).toBe('error(hi)->reject(hi)'); | ||
}); | ||
}); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should probably also test passing an object as the param, e.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've added tests for object params. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want to support this (considering it is not supported in native Promises)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I am not too sure too. I decided to also take care of objects as this is also the case for |
||
describe('race (hash)', function() { | ||
it('should do nothing if given an empty array', function() { | ||
q.race({}).then(success(), error()); | ||
expect(mockNextTick.queue.length).toBe(0); | ||
expect(logStr()).toBe(''); | ||
}); | ||
|
||
it('should resolve as soon as the first promise is settled by resolution', function() { | ||
var deferred1 = defer(), | ||
deferred2 = defer(); | ||
|
||
q.race({a: promise, b: deferred1.promise, c: deferred2.promise}).then(success(), error()); | ||
expect(logStr()).toBe(''); | ||
syncResolve(deferred1, 'hi'); | ||
expect(logStr()).toBe('success(hi)->hi'); | ||
syncResolve(deferred2, 'cau'); | ||
expect(logStr()).toBe('success(hi)->hi'); | ||
syncReject(deferred, 'hola'); | ||
expect(logStr()).toBe('success(hi)->hi'); | ||
}); | ||
|
||
it('should reject as soon as the first promise is settled by rejection', function() { | ||
var deferred1 = defer(), | ||
deferred2 = defer(); | ||
|
||
q.race({a: promise, b: deferred1.promise, c: deferred2.promise}).then(success(), error()); | ||
expect(logStr()).toBe(''); | ||
syncReject(deferred1, 'hi'); | ||
expect(logStr()).toBe('error(hi)->reject(hi)'); | ||
syncResolve(deferred2, 'cau'); | ||
expect(logStr()).toBe('error(hi)->reject(hi)'); | ||
syncReject(deferred, 'hola'); | ||
expect(logStr()).toBe('error(hi)->reject(hi)'); | ||
}); | ||
}); | ||
|
||
describe('exception logging', function() { | ||
var mockExceptionLogger = { | ||
log: [], | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This description is a little missleading. It gives the impression we are looking for the first promise to actually resolve (not reject).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about something like this:
Note that the term
settled
is also used in the spec.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just realized that we are using
resolved
inconsistently in the docs (either asfulfilled
orresolved
in the sense of the spec) 😒 (TBH, I myself thoughtresolved
meansfulfilled
until now...)I would suggest being a little more explicit:
Don't forget to also update the description above 😇