summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJorge Bay Gondra <jorgebaygondra@gmail.com>2016-02-25 20:39:06 +0100
committerJorge Bay Gondra <jorgebaygondra@gmail.com>2016-02-26 12:09:33 +0100
commit91a7e520e5cd514db53810ce9c1f9563a6566808 (patch)
tree942fb1ede03bad89e5f181e5851f748bb3f37fd5
parenta8c285ba7fbc7084c25c3af9b9eeff1e9b63bd62 (diff)
downloadasync-91a7e520e5cd514db53810ce9c1f9563a6566808.tar.gz
Add race method
-rw-r--r--README.md41
-rw-r--r--lib/index.js3
-rw-r--r--lib/race.js14
-rw-r--r--mocha_test/race.js70
4 files changed, 128 insertions, 0 deletions
diff --git a/README.md b/README.md
index 9763875..06ec85b 100644
--- a/README.md
+++ b/README.md
@@ -221,6 +221,7 @@ Some functions are also available in the following forms:
* [`retry`](#retry)
* [`iterator`](#iterator)
* [`times`](#times), `timesSeries`, `timesLimit`
+* [`race`](#race)
### Utils
@@ -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])
diff --git a/lib/index.js b/lib/index.js
index a165909..2e27f25 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -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';
@@ -106,6 +107,7 @@ export default {
parallelLimit: parallelLimit,
priorityQueue: priorityQueue,
queue: queue,
+ race: race,
reduce: reduce,
reduceRight: reduceRight,
reject: reject,
@@ -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,
diff --git a/lib/race.js b/lib/race.js
new file mode 100644
index 0000000..4a5f8a5
--- /dev/null
+++ b/lib/race.js
@@ -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);
+ }
+}
diff --git a/mocha_test/race.js b/mocha_test/race.js
new file mode 100644
index 0000000..d56205d
--- /dev/null
+++ b/mocha_test/race.js
@@ -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);
+ });
+});
+