summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Early <alexander.early@gmail.com>2016-06-07 13:45:49 -0700
committerAlexander Early <alexander.early@gmail.com>2016-06-07 13:45:49 -0700
commit0523b93a231c77961d0cba2f0bbf0e794c75d6fb (patch)
tree53388c4e7a96bded8deaf96008235f7e8b2d4640
parent587ee1ea2e1fee83fa813500f03ce08b40582fa7 (diff)
parent872cb9133882302627e989432f3aa7a028ccfa3f (diff)
downloadasync-0523b93a231c77961d0cba2f0bbf0e794c75d6fb.tar.gz
Merge branch 'master' of ssh://github.com/caolan/async
-rw-r--r--.gitattributes2
-rw-r--r--lib/index.js9
-rw-r--r--lib/internal/map.js9
-rw-r--r--lib/internal/setImmediate.js33
-rw-r--r--lib/map.js6
-rw-r--r--lib/mapValues.js46
-rw-r--r--lib/mapValuesLimit.js33
-rw-r--r--lib/mapValuesSeries.js21
-rw-r--r--lib/nextTick.js16
-rw-r--r--mocha_test/ensureAsync.js170
-rw-r--r--mocha_test/map.js18
-rw-r--r--mocha_test/mapValues.js92
12 files changed, 340 insertions, 115 deletions
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..4cab1f4
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+# Set the default behavior, in case people don't have core.autocrlf set.
+* text=auto
diff --git a/lib/index.js b/lib/index.js
index 1180dcf..091f657 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -43,6 +43,9 @@ import log from './log';
import map from './map';
import mapLimit from './mapLimit';
import mapSeries from './mapSeries';
+import mapValues from './mapValues';
+import mapValuesLimit from './mapValuesLimit';
+import mapValuesSeries from './mapValuesSeries';
import memoize from './memoize';
import nextTick from './nextTick';
import parallel from './parallel';
@@ -115,6 +118,9 @@ export default {
map: map,
mapLimit: mapLimit,
mapSeries: mapSeries,
+ mapValues: mapValues,
+ mapValuesLimit: mapValuesLimit,
+ mapValuesSeries: mapValuesSeries,
memoize: memoize,
nextTick: nextTick,
parallel: parallel,
@@ -205,6 +211,9 @@ export {
map as map,
mapLimit as mapLimit,
mapSeries as mapSeries,
+ mapValues as mapValues,
+ mapValuesLimit as mapValuesLimit,
+ mapValuesSeries as mapValuesSeries,
memoize as memoize,
nextTick as nextTick,
parallel as parallel,
diff --git a/lib/internal/map.js b/lib/internal/map.js
index 45c3eae..2c2a750 100644
--- a/lib/internal/map.js
+++ b/lib/internal/map.js
@@ -1,13 +1,14 @@
-import isArrayLike from 'lodash/isArrayLike';
-import getIterator from './getIterator';
import noop from 'lodash/noop';
import once from './once';
export default function _asyncMap(eachfn, arr, iteratee, callback) {
callback = once(callback || noop);
arr = arr || [];
- var results = isArrayLike(arr) || getIterator(arr) ? [] : {};
- eachfn(arr, function (value, index, callback) {
+ var results = [];
+ var counter = 0;
+
+ eachfn(arr, function (value, _, callback) {
+ var index = counter++;
iteratee(value, function (err, v) {
results[index] = v;
callback(err);
diff --git a/lib/internal/setImmediate.js b/lib/internal/setImmediate.js
index 7d15249..eb4ea63 100644
--- a/lib/internal/setImmediate.js
+++ b/lib/internal/setImmediate.js
@@ -1,21 +1,30 @@
'use strict';
+
import rest from 'lodash/rest';
-var _setImmediate = typeof setImmediate === 'function' && setImmediate;
+export var hasSetImmediate = typeof setImmediate === 'function' && setImmediate;
+export var hasNextTick = typeof process === 'object' && typeof process.nextTick === 'function';
+
+export function fallback(fn) {
+ setTimeout(fn, 0);
+}
+
+export function wrap(defer) {
+ return rest(function (fn, args) {
+ defer(function () {
+ fn.apply(null, args);
+ });
+ });
+}
var _defer;
-if (_setImmediate) {
- _defer = _setImmediate;
-} else if (typeof process === 'object' && typeof process.nextTick === 'function') {
+
+if (hasSetImmediate) {
+ _defer = setImmediate;
+} else if (hasNextTick) {
_defer = process.nextTick;
} else {
- _defer = function(fn) {
- setTimeout(fn, 0);
- };
+ _defer = fallback;
}
-export default rest(function (fn, args) {
- _defer(function () {
- fn.apply(null, args);
- });
-});
+export default wrap(_defer);
diff --git a/lib/map.js b/lib/map.js
index 892456b..19642c8 100644
--- a/lib/map.js
+++ b/lib/map.js
@@ -14,6 +14,10 @@ import doLimit from './internal/doLimit';
* in order. However, the results array will be in the same order as the
* original `coll`.
*
+ * If `map` is passed an Object, the results will be an Array. The results
+ * will roughly be in the order of the original Objects' keys (but this can
+ * vary across JavaScript engines)
+ *
* @name map
* @static
* @memberOf async
@@ -24,7 +28,7 @@ import doLimit from './internal/doLimit';
* once it has completed with an error (which can be `null`) and a
* transformed item. Invoked with (item, callback).
* @param {Function} [callback] - A callback which is called when all `iteratee`
- * functions have finished, or an error occurs. Results is an array of the
+ * functions have finished, or an error occurs. Results is an Array of the
* transformed items from the `coll`. Invoked with (err, results).
* @example
*
diff --git a/lib/mapValues.js b/lib/mapValues.js
new file mode 100644
index 0000000..83ffc43
--- /dev/null
+++ b/lib/mapValues.js
@@ -0,0 +1,46 @@
+import mapValuesLimit from './mapValuesLimit';
+import doLimit from './internal/doLimit';
+
+
+/**
+ * A relative of `map`, designed for use with objects.
+ *
+ * Produces a new Object by mapping each value of `obj` through the `iteratee`
+ * function. The `iteratee` is called each `value` and `key` from `obj` and a
+ * callback for when it has finished processing. Each of these callbacks takes
+ * two arguments: an `error`, and the transformed item from `obj`. If `iteratee`
+ * passes an error to its callback, the main `callback` (for the `mapValues`
+ * function) is immediately called with the error.
+ *
+ * Note, the order of the keys in the result is not guaranteed. The keys will
+ * be roughly in the order they complete, (but this is very engine-specific)
+ *
+ * @name mapValues
+ * @static
+ * @memberOf async
+ * @category Collection
+ * @param {Object} obj - A collection to iterate over.
+ * @param {Function} iteratee - A function to apply to each value and key in
+ * `coll`. The iteratee is passed a `callback(err, transformed)` which must be
+ * called once it has completed with an error (which can be `null`) and a
+ * transformed value. Invoked with (value, key, callback).
+ * @param {Function} [callback] - A callback which is called when all `iteratee`
+ * functions have finished, or an error occurs. Results is an array of the
+ * transformed items from the `obj`. Invoked with (err, result).
+ * @example
+ *
+ * async.mapValues({
+ * f1: 'file1',
+ * f2: 'file2',
+ * f3: 'file3'
+ * }, fs.stat, function(err, result) {
+ * // results is now a map of stats for each file, e.g.
+ * // {
+ * // f1: [stats for file1],
+ * // f2: [stats for file2],
+ * // f3: [stats for file3]
+ * // }
+ * });
+ */
+
+export default doLimit(mapValuesLimit, Infinity);
diff --git a/lib/mapValuesLimit.js b/lib/mapValuesLimit.js
new file mode 100644
index 0000000..762b871
--- /dev/null
+++ b/lib/mapValuesLimit.js
@@ -0,0 +1,33 @@
+import eachOfLimit from './eachOfLimit';
+
+/**
+ * The same as `mapValues` but runs a maximum of `limit` async operations at a
+ * time.
+ *
+ * @name mapValuesLimit
+ * @static
+ * @memberOf async
+ * @see async.mapValues
+ * @category Collection
+ * @param {Object} obj - A collection to iterate over.
+ * @param {number} limit - The maximum number of async operations at a time.
+ * @param {Function} iteratee - A function to apply to each value in `obj`.
+ * The iteratee is passed a `callback(err, transformed)` which must be called
+ * once it has completed with an error (which can be `null`) and a
+ * transformed value. Invoked with (value, key, callback).
+ * @param {Function} [callback] - A callback which is called when all `iteratee`
+ * functions have finished, or an error occurs. Result is an object of the
+ * transformed values from the `obj`. Invoked with (err, result).
+ */
+export default function mapValuesLimit(obj, limit, iteratee, callback) {
+ var newObj = {};
+ eachOfLimit(obj, limit, function(val, key, next) {
+ iteratee(val, key, function (err, result) {
+ if (err) return next(err);
+ newObj[key] = result;
+ next();
+ });
+ }, function (err) {
+ callback(err, newObj);
+ });
+}
diff --git a/lib/mapValuesSeries.js b/lib/mapValuesSeries.js
new file mode 100644
index 0000000..163d474
--- /dev/null
+++ b/lib/mapValuesSeries.js
@@ -0,0 +1,21 @@
+import mapValuesLimit from './mapValuesLimit';
+import doLimit from './internal/doLimit';
+
+/**
+ * The same as `mapValues` but runs only a single async operation at a time.
+ *
+ * @name mapValuesSeries
+ * @static
+ * @memberOf async
+ * @see async.mapValues
+ * @category Collection
+ * @param {Object} obj - A collection to iterate over.
+ * @param {Function} iteratee - A function to apply to each value in `obj`.
+ * The iteratee is passed a `callback(err, transformed)` which must be called
+ * once it has completed with an error (which can be `null`) and a
+ * transformed value. Invoked with (value, key, callback).
+ * @param {Function} [callback] - A callback which is called when all `iteratee`
+ * functions have finished, or an error occurs. Result is an object of the
+ * transformed values from the `obj`. Invoked with (err, result).
+ */
+export default doLimit(mapValuesLimit, 1);
diff --git a/lib/nextTick.js b/lib/nextTick.js
index 66a4c1f..a69b87e 100644
--- a/lib/nextTick.js
+++ b/lib/nextTick.js
@@ -1,4 +1,6 @@
-import setImmediate from './internal/setImmediate';
+'use strict';
+
+import { hasNexTick, hasSetImmediate, fallback, wrap } from './internal/setImmediate';
/**
* Calls `callback` on a later loop around the event loop. In Node.js this just
@@ -30,4 +32,14 @@ import setImmediate from './internal/setImmediate';
* // a, b, and c equal 1, 2, and 3
* }, 1, 2, 3);
*/
-export default setImmediate;
+var _defer;
+
+if (hasNexTick) {
+ _defer = process.nextTick;
+} else if (hasSetImmediate) {
+ _defer = setImmediate;
+} else {
+ _defer = fallback;
+}
+
+export default wrap(_defer);
diff --git a/mocha_test/ensureAsync.js b/mocha_test/ensureAsync.js
index fd8cc3f..89659e2 100644
--- a/mocha_test/ensureAsync.js
+++ b/mocha_test/ensureAsync.js
@@ -1,85 +1,85 @@
-var async = require('../lib');
-var expect = require('chai').expect;
-var assert = require('assert');
-
-describe('ensureAsync', function() {
- var passContext = function(cb) {
- cb(this);
- };
-
- it('defer sync functions', function(done) {
- var sync = true;
- async.ensureAsync(function (arg1, arg2, cb) {
- expect(arg1).to.equal(1);
- expect(arg2).to.equal(2);
- cb(null, 4, 5);
- })(1, 2, function (err, arg4, arg5) {
- expect(err).to.equal(null);
- expect(arg4).to.equal(4);
- expect(arg5).to.equal(5);
- assert(!sync, 'callback called on same tick');
- done();
- });
- sync = false;
- });
-
- it('do not defer async functions', function(done) {
- var sync = false;
- async.ensureAsync(function (arg1, arg2, cb) {
- expect(arg1).to.equal(1);
- expect(arg2).to.equal(2);
- async.setImmediate(function () {
- sync = true;
- cb(null, 4, 5);
- sync = false;
- });
- })(1, 2, function (err, arg4, arg5) {
- expect(err).to.equal(null);
- expect(arg4).to.equal(4);
- expect(arg5).to.equal(5);
- assert(sync, 'callback called on next tick');
- done();
- });
- });
-
- it('double wrapping', function(done) {
- var sync = true;
- async.ensureAsync(async.ensureAsync(function (arg1, arg2, cb) {
- expect(arg1).to.equal(1);
- expect(arg2).to.equal(2);
- cb(null, 4, 5);
- }))(1, 2, function (err, arg4, arg5) {
- expect(err).to.equal(null);
- expect(arg4).to.equal(4);
- expect(arg5).to.equal(5);
- assert(!sync, 'callback called on same tick');
- done();
- });
- sync = false;
- });
-
-
- it('should propely bind context to the wrapped function', function(done) {
-
- // call bind after wrapping with ensureAsync
- var context = {context: "post"};
- var postBind = async.ensureAsync(passContext);
- postBind = postBind.bind(context);
- postBind(function(ref) {
- expect(ref).to.equal(context);
- done();
- });
- });
-
- it('should not override the bound context of a function when wrapping', function(done) {
-
- // call bind before wrapping with ensureAsync
- var context = {context: "pre"};
- var preBind = passContext.bind(context);
- preBind = async.ensureAsync(preBind);
- preBind(function(ref) {
- expect(ref).to.equal(context);
- done();
- });
- });
-});
+var async = require('../lib');
+var expect = require('chai').expect;
+var assert = require('assert');
+
+describe('ensureAsync', function() {
+ var passContext = function(cb) {
+ cb(this);
+ };
+
+ it('defer sync functions', function(done) {
+ var sync = true;
+ async.ensureAsync(function (arg1, arg2, cb) {
+ expect(arg1).to.equal(1);
+ expect(arg2).to.equal(2);
+ cb(null, 4, 5);
+ })(1, 2, function (err, arg4, arg5) {
+ expect(err).to.equal(null);
+ expect(arg4).to.equal(4);
+ expect(arg5).to.equal(5);
+ assert(!sync, 'callback called on same tick');
+ done();
+ });
+ sync = false;
+ });
+
+ it('do not defer async functions', function(done) {
+ var sync = false;
+ async.ensureAsync(function (arg1, arg2, cb) {
+ expect(arg1).to.equal(1);
+ expect(arg2).to.equal(2);
+ async.setImmediate(function () {
+ sync = true;
+ cb(null, 4, 5);
+ sync = false;
+ });
+ })(1, 2, function (err, arg4, arg5) {
+ expect(err).to.equal(null);
+ expect(arg4).to.equal(4);
+ expect(arg5).to.equal(5);
+ assert(sync, 'callback called on next tick');
+ done();
+ });
+ });
+
+ it('double wrapping', function(done) {
+ var sync = true;
+ async.ensureAsync(async.ensureAsync(function (arg1, arg2, cb) {
+ expect(arg1).to.equal(1);
+ expect(arg2).to.equal(2);
+ cb(null, 4, 5);
+ }))(1, 2, function (err, arg4, arg5) {
+ expect(err).to.equal(null);
+ expect(arg4).to.equal(4);
+ expect(arg5).to.equal(5);
+ assert(!sync, 'callback called on same tick');
+ done();
+ });
+ sync = false;
+ });
+
+
+ it('should propely bind context to the wrapped function', function(done) {
+
+ // call bind after wrapping with ensureAsync
+ var context = {context: "post"};
+ var postBind = async.ensureAsync(passContext);
+ postBind = postBind.bind(context);
+ postBind(function(ref) {
+ expect(ref).to.equal(context);
+ done();
+ });
+ });
+
+ it('should not override the bound context of a function when wrapping', function(done) {
+
+ // call bind before wrapping with ensureAsync
+ var context = {context: "pre"};
+ var preBind = passContext.bind(context);
+ preBind = async.ensureAsync(preBind);
+ preBind(function(ref) {
+ expect(ref).to.equal(context);
+ done();
+ });
+ });
+});
diff --git a/mocha_test/map.js b/mocha_test/map.js
index a537427..e7d4fa3 100644
--- a/mocha_test/map.js
+++ b/mocha_test/map.js
@@ -122,12 +122,10 @@ describe("map", function() {
callback(null, val * 2);
}, function(err, result) {
if (err) throw err;
- expect(Object.prototype.toString.call(result)).to.equal('[object Object]');
- expect(result).to.eql({
- a: 2,
- b: 4,
- c: 6
- });
+ expect(Object.prototype.toString.call(result)).to.equal('[object Array]');
+ expect(result).to.contain(2);
+ expect(result).to.contain(4);
+ expect(result).to.contain(6);
done();
});
});
@@ -170,11 +168,9 @@ describe("map", function() {
callback(null, val * 2);
}, function(err, result) {
if (err) throw err;
- expect(result).to.eql({
- a: 2,
- b: 4,
- c: 6
- });
+ expect(result).to.contain(2);
+ expect(result).to.contain(4);
+ expect(result).to.contain(6);
done();
});
});
diff --git a/mocha_test/mapValues.js b/mocha_test/mapValues.js
new file mode 100644
index 0000000..44eb27d
--- /dev/null
+++ b/mocha_test/mapValues.js
@@ -0,0 +1,92 @@
+var async = require('../lib');
+var expect = require('chai').expect;
+var assert = require('assert');
+
+describe('mapValues', function () {
+ var obj = {a: 1, b: 2, c: 3};
+
+ context('mapValuesLimit', function () {
+ it('basics', function (done) {
+ var running = 0;
+ var concurrency = {
+ a: 2,
+ b: 2,
+ c: 1
+ };
+ async.mapValuesLimit(obj, 2, function (val, key, next) {
+ running++;
+ async.setImmediate(function () {
+ expect(running).to.equal(concurrency[key]);
+ running--;
+ next(null, key + val);
+ });
+ }, function (err, result) {
+ expect(running).to.equal(0);
+ expect(err).to.eql(null);
+ expect(result).to.eql({a: 'a1', b: 'b2', c: 'c3'});
+ done();
+ });
+ });
+
+ it('error', function (done) {
+ async.mapValuesLimit(obj, 1, function(val, key, next) {
+ if (key === 'b') {
+ return next(new Error("fail"));
+ }
+ next(null, val);
+ }, function (err, result) {
+ expect(err).to.not.eql(null);
+ expect(result).to.eql({a: 1});
+ done();
+ });
+ });
+ });
+
+ context('mapValues', function () {
+ it('basics', function (done) {
+ var running = 0;
+ var concurrency = {
+ a: 3,
+ b: 2,
+ c: 1
+ };
+ async.mapValues(obj, function (val, key, next) {
+ running++;
+ async.setImmediate(function () {
+ expect(running).to.equal(concurrency[key]);
+ running--;
+ next(null, key + val);
+ });
+ }, function (err, result) {
+ expect(running).to.equal(0);
+ expect(err).to.eql(null);
+ expect(result).to.eql({a: 'a1', b: 'b2', c: 'c3'});
+ done();
+ });
+ });
+ });
+
+ context('mapValuesSeries', function () {
+ it('basics', function (done) {
+ var running = 0;
+ var concurrency = {
+ a: 1,
+ b: 1,
+ c: 1
+ };
+ async.mapValuesSeries(obj, function (val, key, next) {
+ running++;
+ async.setImmediate(function () {
+ expect(running).to.equal(concurrency[key]);
+ running--;
+ next(null, key + val);
+ });
+ }, function (err, result) {
+ expect(running).to.equal(0);
+ expect(err).to.eql(null);
+ expect(result).to.eql({a: 'a1', b: 'b2', c: 'c3'});
+ done();
+ });
+ });
+ });
+});