This commit is contained in:
AJ ONeal 2015-01-02 18:08:02 -07:00
parent 0cc859e3d0
commit c543917f07
7 changed files with 171 additions and 96 deletions

View File

@ -5,45 +5,45 @@ Analogous to `[].forEach`, but handles items asynchronously with a final callbac
This is the most essential piece of the [`ArrayAsync`](https://github.com/FuturesJS/ArrayAsync) package. This is the most essential piece of the [`ArrayAsync`](https://github.com/FuturesJS/ArrayAsync) package.
v3.x - Diet Cola Edition
---
As I do every few years, I decided to rewrite FuturesJS. v5.x
This year's remake is extremely lightweight. ----
We jumped from 3.x to 5.x because I'm considering creating a backwards-and-forwards compatible 4.x that
uses AngularJS-style function introspection to allow for having the next param.
Straight up, that's probably a bad idea and waste of time so I hope I don't actually do it.
Usage Usage
=== -----
It's as simple as you could guess:
```javascript ```javascript
// waits for one request to finish before beginning the next // EXAMPLE ASYNC FUNCTION
forEachAsync(['dogs', 'cats', 'octocats'], function (next, element, index, array) {
getPics(element, next);
// then after all of the elements have been handled function getPicsAsync(animal) {
// the final callback fires to let you know it's all done var flickerApi = "http://api.flickr.com/services/feeds/photos_public.gne?tagmode=any&format=json&tags=" + animal;
}).then(function () {
console.log('All requests have finished');
});
// where `getPics` might be an asynchronous web request such as this return requestAsync({ url: flickerApi });
function getPics(animal, cb) {
var flickerAPI = "http://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?";
$.getJSON(
flickerAPI
, { tags: thing
, tagmode: "any"
, format: "json"
, success: function (data) {
console.log('teh animals:', data);
}
, complete: cb
}
);
} }
``` ```
```javascript
forEachAsync(['dogs', 'cats', 'octocats'], function (element) {
return getPicsAsync(element);
}).then(function () {
// then after all of the elements have been handled
// the final callback fires to let you know it's all done
console.log('All requests have finished');
});
```
### Supplying your own Promises Implementation
If native ES6 promises are not available, then you should supply your own Promises/A+
implementation like so:
```javascript
forEachAsync = forEachAsync.create(window.Promise || require('bluebird'));
```
Browser Installation Browser Installation
=== ===
@ -51,7 +51,7 @@ Browser Installation
You can install from bower: You can install from bower:
```bash ```bash
bower install forEachAsync bower install --save forEachAsync@5.x
``` ```
Or download the raw file from <https://raw.github.com/FuturesJS/forEachAsync/master/forEachAsync.js>: Or download the raw file from <https://raw.github.com/FuturesJS/forEachAsync/master/forEachAsync.js>:
@ -71,35 +71,13 @@ wget https://raw.github.com/FuturesJS/forEachAsync/master/forEachAsync.js
}()); }());
``` ```
Or you can build it alongside other libraries: **Note**: If you need both 3.x/4.x and 5.x version of `forEachAsync` in the browser... good luck with that...
```bash
npm install -g pakmanager
npm install forEachAsync --save
pakmanager -e browser build
```
```html
<script src="pakmanaged.js"></script>
```
```javascript
(function () {
'use strict';
var forEachAsync = require('forEachAsync').forEachAsync
;
// do stuff ...
}());
```
Node Installation Node Installation
=== ===
```bash ```bash
npm install --save forEachAsync@3.x npm install --save forEachAsync@5.x
``` ```
API API
@ -111,7 +89,6 @@ Parameters
* `array` Array of elements to iterate over * `array` Array of elements to iterate over
* `callback` Function to execute for each element, takes 4 arguments * `callback` Function to execute for each element, takes 4 arguments
* `next` the function to call when the current element has been dealt with
* `element` a single element of the aforementioned array * `element` a single element of the aforementioned array
* `index` the index of the current element * `index` the index of the current element
* `array` the same array mentioned above * `array` the same array mentioned above

View File

@ -11,14 +11,16 @@ window.addEventListener('load', function () {
; ;
log('i', 'item', 'ms'); log('i', 'item', 'ms');
forEachAsync([2, 11, 37, 42], function (next, item, i) { forEachAsync([2, 11, 37, 42], function (item, i) {
var ms = Math.floor(Math.random() * 1000) var ms = Math.floor(Math.random() * 1000)
; ;
return new Promise(function (resolve) {
setTimeout(function () { setTimeout(function () {
log(i, item, ms); log(i, item, ms);
next(); resolve();
}, ms); }, ms);
});
}).then(function () { }).then(function () {
log('All Done'); log('All Done');
}); });

View File

@ -1,20 +1,20 @@
'use strict'; 'use strict';
var fs = require('fs') var PromiseA = require('bluebird')
, fs = PromiseA.promisifyAll(require('fs'))
, forEachAsync = require('foreachasync').forEachAsync , forEachAsync = require('foreachasync').forEachAsync
, path = require('path') , path = require('path')
, dirpath = path.join(__dirname, 'testfiles') , dirpath = path.join(__dirname, 'testfiles')
; ;
fs.readdir(dirpath, function (err, nodes) { fs.readdir(dirpath, function (err, nodes) {
forEachAsync(nodes, function (next, node) { forEachAsync(nodes, function (node) {
var filepath = path.join(dirpath, node) var filepath = path.join(dirpath, node)
; ;
console.log(filepath); console.log(filepath);
fs.readFile(filepath, null, function (err, contents) { return fs.readFileAsync(filepath, null).then(function (contents) {
console.log(node, contents.length); console.log(node, contents.length);
next();
}); });
}).then(function () { }).then(function () {
console.log('All Done!'); console.log('All Done!');

View File

@ -2,34 +2,89 @@
;(function (exports) { ;(function (exports) {
'use strict'; 'use strict';
function forEachAsync(arr, fn, thisArg) { var BREAK = {}
var dones = [] , exp = {}
, index = -1
; ;
function next(BREAK, result) { function create(PromiseA) {
index += 1; PromiseA = PromiseA.Promise || PromiseA;
if (index === arr.length || BREAK === forEachAsync.__BREAK) {
dones.forEach(function (done) { function forEachAsync(arr, fn, thisArg) {
done.call(thisArg, result); var result = PromiseA.resolve()
;
arr.forEach(function (item, k) {
result = result.then(function () {
var ret
;
if (thisArg) {
ret = fn.call(thisArg, item, k, arr);
} else {
ret = result = fn(item, k, arr);
}
if (!ret.then) {
ret = PromiseA.resolve(result);
}
ret.then(function (val) {
if (val === forEachAsync.__BREAK) {
return PromiseA.reject(new Error('break'));
//throw new Error('break');
}
return val;
}); });
return; });
});
result.catch(function (e) {
if ('break' !== e.message) {
throw e;
}
});
return result;
} }
fn.call(thisArg, next, arr[index], index, arr); forEachAsync.__BREAK = BREAK;
return forEachAsync;
} }
setTimeout(next, 4); /*
exp = forEachAsync.forEachAsync = forEachAsync;
exports = exports.forEachAsync = forEachAsync.forEachAsycn = forEachAsync;
exports.create = forEachAsync.create = function () {};
*/
return {
then: function (_done) { try {
dones.push(_done); exp.forEachAsync = create(require('bluebird'));
return this; } catch(e) {
if ('undefined' !== typeof PromiseA) {
exp.forEachAsync = create(Promise);
} else {
try {
exp.forEachAsync = create(require('es6-promise'));
} catch(e) {
try {
exp.forEachAsync = create(require('rsvp'));
} catch(e) {
console.warning('forEachAsync needs requires a promise implementation and your environment does not provide one.'
+ '\nYou may provide your own by calling forEachAsync.create(Promise) with a PromiseA+ implementation'
);
} }
}
}
}
exports.forEachAsync = exp.forEachAsync.forEachAsync = exp.forEachAsync || function () {
throw new Error("You did not supply a Promises/A+ implementation. See the warning above.");
}; };
} exports.forEachAsync.create = create;
forEachAsync.__BREAK = {};
exports.forEachAsync = forEachAsync;
}('undefined' !== typeof exports && exports || new Function('return this')())); }('undefined' !== typeof exports && exports || new Function('return this')()));

View File

@ -1,7 +1,7 @@
{ {
"name": "foreachasync", "name": "foreachasync",
"version": "3.0.0", "version": "5.0.0",
"description": "A node- and browser-ready async counterpart of Array.prototype.forEach", "description": "A node- and browser-ready async (now with promises) counterpart of Array.prototype.forEach",
"homepage": "https://github.com/FuturesJS/forEachAsync", "homepage": "https://github.com/FuturesJS/forEachAsync",
"main": "forEachAsync.js", "main": "forEachAsync.js",
"directories": { "directories": {
@ -21,11 +21,18 @@
"forEachAsync", "forEachAsync",
"async", "async",
"futures", "futures",
"promise",
"promises",
"each" "each"
], ],
"optionalDependencies": {
"bluebird": "^2.5.3"
},
"author": "AJ ONeal <coolaj86@gmail.com> (http://coolaj86.com/)", "author": "AJ ONeal <coolaj86@gmail.com> (http://coolaj86.com/)",
"license": "Apache2", "license": "Apache2",
"bugs": { "bugs": {
"url": "https://github.com/FuturesJS/forEachAsync/issues" "url": "https://github.com/FuturesJS/forEachAsync/issues"
},
"dependencies": {
} }
} }

32
test-bluebird.js Normal file
View File

@ -0,0 +1,32 @@
(function () {
"use strict";
var PromiseA = require('bluebird')
, forEachAsync = require('./forEachAsync').forEachAsync
, context = {}
;
forEachAsync([0, 500, 70, 200, 400, 100], function (element, i, arr) {
// test that array order is as expected
console.log(element, 'is element', i, 'of', arr.length);
// test that thisness is applied
this[element] = i;
if (i > 2) {
// test that synchronous callbacks don't mess things up
return PromiseA.resolve();
} else {
// test asynchronous callbacks
return new Promise(function (resolve/*, reject*/) {
setTimeout(resolve, element);
});
}
}, context).then(function () {
// test that thisness carried
console.log('context', context);
}).then(function () {
// test then chaining
console.log("now wasn't that nice?");
});
}());

View File

@ -1,10 +1,11 @@
(function () { (function () {
"use strict"; "use strict";
var forEachAsync = require('./forEachAsync').forEachAsync var forEachAsync = require('./forEachAsync').forEachAsync.create(Promise)
, context = {}
; ;
forEachAsync([0, 500, 70, 200, 400, 100], function (next, element, i, arr) { forEachAsync([0, 500, 70, 200, 400, 100], function (element, i, arr) {
// test that array order is as expected // test that array order is as expected
console.log(element, 'is element', i, 'of', arr.length); console.log(element, 'is element', i, 'of', arr.length);
@ -13,17 +14,18 @@
if (i > 2) { if (i > 2) {
// test that synchronous callbacks don't mess things up // test that synchronous callbacks don't mess things up
next(); return Promise.resolve();
} else { } else {
// test asynchronous callbacks // test asynchronous callbacks
setTimeout(next, element); return new Promise(function (resolve/*, reject*/) {
setTimeout(resolve, element);
});
} }
}, {}).then(function () { }, context).then(function () {
// test that thisness carries // test that thisness carried
console.log(this); console.log('context', context);
}).then(function () { }).then(function () {
// test then chaining // test then chaining
console.log("now wasn't that nice?"); console.log("now wasn't that nice?");
}); });
}()); }());