Skip to content

Commit

Permalink
Merge pull request #1038 from jorgebay/race-module
Browse files Browse the repository at this point in the history
async.race module
  • Loading branch information
aearly committed Feb 27, 2016
2 parents a8c285b + 91a7e52 commit d1ef64c
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 0 deletions.
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ Some functions are also available in the following forms:
* [`retry`](#retry)
* [`iterator`](#iterator)
* [`times`](#times), `timesSeries`, `timesLimit`
* [`race`](#race)
### Utils
Expand Down Expand Up @@ -1655,6 +1656,46 @@ __Related__
---------------------------------------
<a name="race" />
### race(tasks, [callback])
Runs the `tasks` array of functions in parallel, without waiting until the
previous function has completed. Once any the `tasks` completed or pass an
error to its callback, the main `callback` is immediately called. It's
equivalent to `Promise.race()`.
__Arguments__
* `tasks` - An array containing functions to run. Each function is passed
a `callback(err, result)` which it must call on completion with an error `err`
(which can be `null`) and an optional `result` value.
* `callback(err, result)` - A callback to run once any of the
functions have completed. This function gets an error or result from the
first function that completed.
__Example__
```js
async.race([
function(callback){
setTimeout(function(){
callback(null, 'one');
}, 200);
},
function(callback){
setTimeout(function(){
callback(null, 'two');
}, 100);
}
],
// main callback
function(err, result){
// the result will be equal to 'two' as it finishes earlier
});
```
---------------------------------------
<a name="memoize"></a>
### memoize(fn, [hasher])
Expand Down
3 changes: 3 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import parallel from './parallel';
import parallelLimit from './parallelLimit';
import priorityQueue from './priorityQueue';
import queue from './queue';
import race from './race';
import reduce from './reduce';
import reduceRight from './reduceRight';
import reject from './reject';
Expand Down Expand Up @@ -106,6 +107,7 @@ export default {
parallelLimit: parallelLimit,
priorityQueue: priorityQueue,
queue: queue,
race: race,
reduce: reduce,
reduceRight: reduceRight,
reject: reject,
Expand Down Expand Up @@ -188,6 +190,7 @@ export {
parallelLimit as parallelLimit,
priorityQueue as priorityQueue,
queue as queue,
race as race,
reduce as reduce,
reduceRight as reduceRight,
reject as reject,
Expand Down
14 changes: 14 additions & 0 deletions lib/race.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use strict';

import isArray from 'lodash/isArray';
import noop from 'lodash/noop';
import once from 'lodash/once';

export default function race(tasks, cb) {
cb = once(cb || noop);
if (!isArray(tasks)) return cb(new TypeError('First argument to race must be an array of functions'));
if (!tasks.length) return cb();
for (let i = 0; i < tasks.length; i++) {
tasks[i](cb);
}
}
70 changes: 70 additions & 0 deletions mocha_test/race.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
var async = require('../lib');
var assert = require('assert');

describe('race', function () {
it('should call each function in parallel and callback with first result', function raceTest10(done) {
var finished = 0;
var tasks = [];
function eachTest(i) {
var index = i;
return function (next) {
finished++;
next(null, index);
};
}
for (var i = 0; i < 10; i++) {
tasks[i] = eachTest(i);
}
async.race(tasks, function (err, result) {
assert.ifError(err);
//0 finished first
assert.strictEqual(result, 0);
assert.strictEqual(finished, 1);
async.setImmediate(function () {
assert.strictEqual(finished, 10);
done();
});
});
});
it('should callback with the first error', function raceTest20(done) {
var tasks = [];
function eachTest(i) {
var index = i;
return function (next) {
setTimeout(function () {
next(new Error('ERR' + index));
}, 50 - index * 2);
};
}
for (var i = 0; i <= 5; i++) {
tasks[i] = eachTest(i);
}
async.race(tasks, function (err, result) {
assert.ok(err);
assert.ok(err instanceof Error);
assert.strictEqual(typeof result, 'undefined');
assert.strictEqual(err.message, 'ERR5');
done();
});
});
it('should callback when task is empty', function raceTest30(done) {
async.race([], function (err, result) {
assert.ifError(err);
assert.strictEqual(typeof result, 'undefined');
done();
});
});
it('should callback in error the task arg is not an Array', function raceTest40() {
var errors = [];
async.race(null, function (err) {
errors.push(err);
});
async.race({}, function (err) {
errors.push(err);
});
assert.strictEqual(errors.length, 2);
assert.ok(errors[0] instanceof TypeError);
assert.ok(errors[1] instanceof TypeError);
});
});

0 comments on commit d1ef64c

Please sign in to comment.