Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Completing forEachOf #705

Merged
merged 6 commits into from
May 19, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 66 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ Usage:
* [`each`](#each)
* [`eachSeries`](#eachSeries)
* [`eachLimit`](#eachLimit)
* [`forEachOf`](#forEachOf)
* [`forEachOfSeries`](#forEachOfSeries)
* [`forEachOfLimit`](#forEachOfLimit)
* [`map`](#map)
* [`mapSeries`](#mapSeries)
* [`mapLimit`](#mapLimit)
Expand Down Expand Up @@ -191,7 +194,8 @@ __Arguments__
* `iterator(item, callback)` - A function to apply to each item in `arr`.
The iterator is passed a `callback(err)` which must be called once it has
completed. If no error has occurred, the `callback` should be run without
arguments or with an explicit `null` argument.
arguments or with an explicit `null` argument. The array index is not passed
to the iterator. If you need the index, use [`forEachOf`](#forEachOf).
* `callback(err)` - A callback which is called when all `iterator` functions
have finished, or an error occurs.

Expand Down Expand Up @@ -280,6 +284,67 @@ async.eachLimit(documents, 20, requestApi, function(err){
});
```

---------------------------------------

<a name="forEachOf" />
<a name="eachOf" />

### forEachOf(obj, iterator, callback)

Like `each`, except that it iterates over objects, and passes the key as the second argument to the iterator.

__Arguments__

* `obj` - An object or array to iterate over.
* `iterator(item, key, callback)` - A function to apply to each item in `obj`.
The `key` is the item's key, or index in the case of an array. The iterator is
passed a `callback(err)` which must be called once it has completed. If no
error has occurred, the callback should be run without arguments or with an
explicit `null` argument.
* `callback(err)` - A callback which is called when all `iterator` functions have finished, or an error occurs.

__Example__

```js
var obj = {dev: "/dev.json", test: "/test.json", prod: "/prod.json"};
var configs = {};

async.forEachOf(obj, function (value, key, callback) {
fs.readFile(__dirname + value, "utf8", function (err, data) {
if (err) return callback(err);
try {
configs[key] = JSON.parse(data);
} catch (e) {
return callback(e);
}
callback();
})
}, function (err) {
if (err) console.error(err.message);
// configs is now a map of JSON data
doSomethingWith(configs);
})
```

---------------------------------------

<a name="forEachOfSeries" />
<a name="eachOfSeries" />

### forEachOfSeries(obj, iterator, callback)

Like [`forEachOf`](#forEachOf), except only one `iterator` is run at a time. The order of execution is not guaranteed for objects, but it will be guaranteed for arrays.

---------------------------------------

<a name="forEachOfLimit" />
<a name="eachOfLimit" />

### forEachOfLimit(obj, limit, iterator, callback)

Like [`forEachOf`](#forEachOf), except the number of `iterator`s running at a given time is controlled by `limit`.


---------------------------------------

<a name="map" />
Expand Down
118 changes: 118 additions & 0 deletions lib/async.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@
return memo;
};

var _forEachOf = function (object, iterator) {
for (key in object) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May i ask why you don't use Object.keys() ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No reason, I kept the original implementation. I was focused more on merge conflicts. _keys would be faster here...

if (object.hasOwnProperty(key)) {
iterator(object[key], key);
}
}
};

var _keys = function (obj) {
if (Object.keys) {
return Object.keys(obj);
Expand Down Expand Up @@ -163,6 +171,7 @@
};
async.forEachSeries = async.eachSeries;


async.eachLimit = function (arr, limit, iterator, callback) {
var fn = _eachLimit(limit);
fn.apply(null, [arr, iterator, callback]);
Expand Down Expand Up @@ -210,6 +219,115 @@
};



async.forEachOf = async.eachOf = function (object, iterator, callback) {
callback = callback || function () {};
var size = object.length || _keys(object).length;
var completed = 0
if (!size) {
return callback();
}
_forEachOf(object, function (value, key) {
iterator(object[key], key, function (err) {
if (err) {
callback(err);
callback = function () {};
} else {
completed += 1;
if (completed === size) {
callback(null);
}
}
});
});
};

async.forEachOfSeries = async.eachOfSeries = function (obj, iterator, callback) {
callback = callback || function () {};
var keys = _keys(obj);
var size = keys.length;
if (!size) {
return callback();
}
var completed = 0;
var iterate = function () {
var sync = true;
var key = keys[completed];
iterator(obj[key], key, function (err) {
if (err) {
callback(err);
callback = function () {};
}
else {
completed += 1;
if (completed >= size) {
callback(null);
}
else {
if (sync) {
async.nextTick(iterate);
}
else {
iterate();
}
}
}
});
sync = false;
};
iterate();
};



async.forEachOfLimit = async.eachOfLimit = function (obj, limit, iterator, callback) {
_forEachOfLimit(limit)(obj, iterator, callback);
};

var _forEachOfLimit = function (limit) {

return function (obj, iterator, callback) {
callback = callback || function () {};
var keys = _keys(obj);
var size = keys.length;
if (!size || limit <= 0) {
return callback();
}
var completed = 0;
var started = 0;
var running = 0;

(function replenish () {
if (completed >= size) {
return callback();
}

while (running < limit && started < size) {
started += 1;
running += 1;
var key = keys[started - 1];
iterator(obj[key], key, function (err) {
if (err) {
callback(err);
callback = function () {};
}
else {
completed += 1;
running -= 1;
if (completed >= size) {
callback();
}
else {
replenish();
}
}
});
}
})();
};
};


var doParallel = function (fn) {
return function () {
var args = Array.prototype.slice.call(arguments);
Expand Down
Loading