195 lines
4.9 KiB
Markdown
195 lines
4.9 KiB
Markdown
# JavaScript Sync/Async forEach
|
|
|
|
An optionally-asynchronous forEach with an interesting interface.
|
|
|
|
## Getting Started
|
|
|
|
This code should work just fine in Node.js:
|
|
|
|
First, install the module with: `npm install async-foreach`
|
|
|
|
```javascript
|
|
var forEach = require('async-foreach').forEach;
|
|
forEach(["a", "b", "c"], function(item, index, arr) {
|
|
console.log("each", item, index, arr);
|
|
});
|
|
// logs:
|
|
// each a 0 ["a", "b", "c"]
|
|
// each b 1 ["a", "b", "c"]
|
|
// each c 2 ["a", "b", "c"]
|
|
```
|
|
|
|
Or in the browser:
|
|
|
|
```html
|
|
<script src="dist/ba-foreach.min.js"></script>
|
|
<script>
|
|
forEach(["a", "b", "c"], function(item, index, arr) {
|
|
console.log("each", item, index, arr);
|
|
});
|
|
// logs:
|
|
// each a 0 ["a", "b", "c"]
|
|
// each b 1 ["a", "b", "c"]
|
|
// each c 2 ["a", "b", "c"]
|
|
</script>
|
|
```
|
|
|
|
In the browser, you can attach the forEach method to any object.
|
|
|
|
```html
|
|
<script>
|
|
this.exports = Bocoup.utils;
|
|
</script>
|
|
<script src="dist/ba-foreach.min.js"></script>
|
|
<script>
|
|
Bocoup.utils.forEach(["a", "b", "c"], function(item, index, arr) {
|
|
console.log("each", item, index, arr);
|
|
});
|
|
// logs:
|
|
// each a 0 ["a", "b", "c"]
|
|
// each b 1 ["a", "b", "c"]
|
|
// each c 2 ["a", "b", "c"]
|
|
</script>
|
|
```
|
|
|
|
## The General Idea (Why I thought this was worth sharing)
|
|
|
|
The idea is to allow the callback to decide _at runtime_ whether the loop will be synchronous or asynchronous. By using `this` in a creative way (in situations where that value isn't already spoken for), an entire control API can be offered without over-complicating function signatures.
|
|
|
|
```javascript
|
|
forEach(arr, function(item, index) {
|
|
// Synchronous.
|
|
});
|
|
|
|
forEach(arr, function(item, index) {
|
|
// Only when `this.async` is called does iteration becomes asynchronous. The
|
|
// loop won't be continued until the `done` function is executed.
|
|
var done = this.async();
|
|
// Continue in one second.
|
|
setTimeout(done, 1000);
|
|
});
|
|
|
|
forEach(arr, function(item, index) {
|
|
// Break out of synchronous iteration early by returning false.
|
|
return index !== 1;
|
|
});
|
|
|
|
forEach(arr, function(item, index) {
|
|
// Break out of asynchronous iteration early...
|
|
var done = this.async();
|
|
// ...by passing false to the done function.
|
|
setTimeout(function() {
|
|
done(index !== 1);
|
|
});
|
|
});
|
|
```
|
|
|
|
## Examples
|
|
See the unit tests for more examples.
|
|
|
|
```javascript
|
|
// Generic "done" callback.
|
|
function allDone(notAborted, arr) {
|
|
console.log("done", notAborted, arr);
|
|
}
|
|
|
|
// Synchronous.
|
|
forEach(["a", "b", "c"], function(item, index, arr) {
|
|
console.log("each", item, index, arr);
|
|
}, allDone);
|
|
// logs:
|
|
// each a 0 ["a", "b", "c"]
|
|
// each b 1 ["a", "b", "c"]
|
|
// each c 2 ["a", "b", "c"]
|
|
// done true ["a", "b", "c"]
|
|
|
|
// Synchronous with early abort.
|
|
forEach(["a", "b", "c"], function(item, index, arr) {
|
|
console.log("each", item, index, arr);
|
|
if (item === "b") { return false; }
|
|
}, allDone);
|
|
// logs:
|
|
// each a 0 ["a", "b", "c"]
|
|
// each b 1 ["a", "b", "c"]
|
|
// done false ["a", "b", "c"]
|
|
|
|
// Asynchronous.
|
|
forEach(["a", "b", "c"], function(item, index, arr) {
|
|
console.log("each", item, index, arr);
|
|
var done = this.async();
|
|
setTimeout(function() {
|
|
done();
|
|
}, 500);
|
|
}, allDone);
|
|
// logs:
|
|
// each a 0 ["a", "b", "c"]
|
|
// each b 1 ["a", "b", "c"]
|
|
// each c 2 ["a", "b", "c"]
|
|
// done true ["a", "b", "c"]
|
|
|
|
// Asynchronous with early abort.
|
|
forEach(["a", "b", "c"], function(item, index, arr) {
|
|
console.log("each", item, index, arr);
|
|
var done = this.async();
|
|
setTimeout(function() {
|
|
done(item !== "b");
|
|
}, 500);
|
|
}, allDone);
|
|
// logs:
|
|
// each a 0 ["a", "b", "c"]
|
|
// each b 1 ["a", "b", "c"]
|
|
// done false ["a", "b", "c"]
|
|
|
|
// Not actually asynchronous.
|
|
forEach(["a", "b", "c"], function(item, index, arr) {
|
|
console.log("each", item, index, arr);
|
|
var done = this.async()
|
|
done();
|
|
}, allDone);
|
|
// logs:
|
|
// each a 0 ["a", "b", "c"]
|
|
// each b 1 ["a", "b", "c"]
|
|
// each c 2 ["a", "b", "c"]
|
|
// done true ["a", "b", "c"]
|
|
|
|
// Not actually asynchronous with early abort.
|
|
forEach(["a", "b", "c"], function(item, index, arr) {
|
|
console.log("each", item, index, arr);
|
|
var done = this.async();
|
|
done(item !== "b");
|
|
}, allDone);
|
|
// logs:
|
|
// each a 0 ["a", "b", "c"]
|
|
// each b 1 ["a", "b", "c"]
|
|
// done false ["a", "b", "c"]
|
|
```
|
|
|
|
## Contributing
|
|
In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using [grunt](https://github.com/cowboy/grunt).
|
|
|
|
_Also, please don't edit files in the "dist" subdirectory as they are generated via grunt. You'll find source code in the "lib" subdirectory!_
|
|
|
|
## Release History
|
|
|
|
04/29/2013
|
|
v0.1.3
|
|
Removed hard Node.js version dependency.
|
|
|
|
11/17/2011
|
|
v0.1.2
|
|
Adding sparse array support.
|
|
Invalid length properties are now sanitized.
|
|
This closes issue #1 (like a boss).
|
|
|
|
11/11/2011
|
|
v0.1.1
|
|
Refactored code to be much simpler. Yay for unit tests!
|
|
|
|
11/11/2011
|
|
v0.1.0
|
|
Initial Release.
|
|
|
|
## License
|
|
Copyright (c) 2012 "Cowboy" Ben Alman
|
|
Licensed under the MIT license.
|
|
<http://benalman.com/about/license/>
|