Compare commits

...

3 Commits

Author SHA1 Message Date
AJ ONeal 178672ad0d v1.0.3: use index as well 2019-11-25 16:05:46 -07:00
AJ ONeal 9beae76631 make Prettier 2019-11-25 15:43:58 -07:00
AJ ONeal dd206891d5 make Prettier 2019-11-25 15:38:55 -07:00
4 changed files with 175 additions and 170 deletions

View File

@ -1,71 +1,70 @@
(function (exports) { (function(exports) {
'use strict'; "use strict";
exports.batchAsync = function (limit, arr, doStuff) { exports.batchAsync = function(limit, arr, doStuff) {
arr = arr.slice(0); arr = arr.slice(0);
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
var total = arr.length; var total = arr.length;
var active = 0; var active = 0;
var results = []; var results = [];
var error; var error;
function doMoreStuff() { function doMoreStuff() {
// Don't take on any more tasks if we've errored, // Don't take on any more tasks if we've errored,
// or if too many are already in progress // or if too many are already in progress
if (error || active > limit) { if (error || active > limit) {
return; return;
} }
// If there are no more tasks to start, return // If there are no more tasks to start, return
if (!arr.length) { if (!arr.length) {
// If everything is also *finished*, resolve // If everything is also *finished*, resolve
if (active < 1) { if (active < 1) {
resolve(results); resolve(results);
} }
return; return;
} }
// We need to dequeue the task here so the index is correct // We need to dequeue the task here so the index is correct
// (keep in mind we want to support sync and async) // (keep in mind we want to support sync and async)
var index = total - arr.length; var index = total - arr.length;
var task = arr.shift(); var task = arr.shift();
active += 1; active += 1;
// Spawn another task immediately, // Spawn another task immediately,
// which will be stopped if we're at the limit // which will be stopped if we're at the limit
doMoreStuff(); doMoreStuff();
var p; var p;
try { try {
p = doStuff(task); p = doStuff(task, index, arr);
} catch (e) { } catch (e) {
// we need to handle, and bubble, synchronous errors // we need to handle, and bubble, synchronous errors
error = e; error = e;
reject(e); reject(e);
throw e; throw e;
} }
// Do stuff and then decrease the active counter when done // Do stuff and then decrease the active counter when done
// add support for sync by rapping in a promise // add support for sync by rapping in a promise
Promise.resolve(p) Promise.resolve(p)
.then(function(result) { .then(function(result) {
if ('undefined' === typeof result) { if ("undefined" === typeof result) {
throw new Error( throw new Error(
"result was 'undefined'. Please return 'null' to signal that you didn't just forget to return another promise." "result was 'undefined'. Please return 'null' to signal that you didn't just forget to return another promise."
); );
} }
active -= 1; active -= 1;
results[index] = result; results[index] = result;
}) })
.then(doMoreStuff) .then(doMoreStuff)
.catch(function(e) { .catch(function(e) {
// handle async errors // handle async errors
error = e; error = e;
reject(e); reject(e);
}); });
} }
doMoreStuff(); doMoreStuff();
}); });
}; };
})("undefined" !== typeof window ? window : module.exports);
}('undefined' !== typeof window ? window : module.exports));

5
package-lock.json generated Normal file
View File

@ -0,0 +1,5 @@
{
"name": "batchasync",
"version": "1.0.3",
"lockfileVersion": 1
}

View File

@ -1,6 +1,6 @@
{ {
"name": "batchasync", "name": "batchasync",
"version": "1.0.2", "version": "1.0.3",
"description": "Like forEachAsync, or Promise.all(), but handling a bounded number of items at any given time.", "description": "Like forEachAsync, or Promise.all(), but handling a bounded number of items at any given time.",
"main": "batchasync.js", "main": "batchasync.js",
"scripts": { "scripts": {

211
test.js
View File

@ -1,110 +1,111 @@
(function (exports) { (function(exports) {
'use strict'; "use strict";
var batchAsync = exports.batchAsync || require('./batchasync.js').batchAsync; var batchAsync =
exports.batchAsync || require("./batchasync.js").batchAsync;
function testBatch() { function testBatch() {
var timeouts = [100, 80, 20, 500, 50, 30, 200, 300]; var timeouts = [100, 80, 20, 500, 50, 30, 200, 300];
var tasks = timeouts.map(function(timeout, i) { console.info(timeouts);
return function() { var tasks = timeouts.map(function(timeout, i) {
return promiseTimeout(timeout).then(function() { return function() {
console.log('task:', i, timeouts[i]); return promiseTimeout(timeout).then(function() {
return i; //console.log("task:", i, timeouts[i]);
}); return i + ":" + timeouts[i];
}; });
}); };
var len = tasks.length; });
var len = tasks.length;
return batchAsync(4, tasks, function(task) { return batchAsync(4, tasks, function(task) {
return task(); return task();
}) })
.then(function(results) { .then(function(results) {
console.info('results:', results); console.info("results:", results);
if (len !== results.length) { if (len !== results.length) {
throw new Error('result set too small'); throw new Error("result set too small");
} }
if (results.join(' ') !== results.sort().join(' ')) { if (results.join(" ") !== results.sort().join(" ")) {
throw new Error('result set out-of-order'); throw new Error("result set out-of-order");
} }
}) })
.then(function() { .then(function() {
return batchAsync(4, [], 'not a function').then(function() { return batchAsync(4, [], "not a function").then(function() {
console.info('Handled ZERO tasks correctly.'); console.info("Handled ZERO tasks correctly.");
}); });
}) })
.then(function() { .then(function() {
return batchAsync(4, timeouts, function(x) { return batchAsync(4, timeouts, function(x) {
return x; return x;
}).then(function(results) { }).then(function(results) {
if (results.join(' ') !== timeouts.join(' ')) { if (results.join(" ") !== timeouts.join(" ")) {
console.error(results); console.error(results);
throw new Error('sync result set out-of-order'); throw new Error("sync result set out-of-order");
} }
console.info('Handled sync tasks correctly.'); console.info("Handled sync tasks correctly.");
}); });
}) })
.then(function() { .then(function() {
return batchAsync(4, tasks, function(task) { return batchAsync(4, tasks, function(task) {
if (0 === Math.floor(Math.random() * 2) % 2) { if (0 === Math.floor(Math.random() * 2) % 2) {
throw new Error('any async error will do'); throw new Error("any async error will do");
} }
return task(); return task();
}) })
.then(function(results) { .then(function(results) {
console.log(results); console.log(results);
var e = new Error('async rejection should not pass!'); var e = new Error("async rejection should not pass!");
e.FAIL = true; e.FAIL = true;
throw e; throw e;
}) })
.catch(function(e) { .catch(function(e) {
if (e.FAIL) { if (e.FAIL) {
throw e; throw e;
} }
console.info('Pass: Exception thrown when expected'); console.info("Pass: Exception thrown when expected");
}); });
}) })
.then(function() { .then(function() {
return batchAsync(4, timeouts, function() { return batchAsync(4, timeouts, function() {
if (0 === Math.floor(Math.random() * 2) % 2) { if (0 === Math.floor(Math.random() * 2) % 2) {
throw new Error('any sync error will do'); throw new Error("any sync error will do");
} }
return null; return null;
}) })
.then(function(results) { .then(function(/*results*/) {
var e = new Error('should not pass sync exception!'); var e = new Error("should not pass sync exception!");
e.FAIL = true; e.FAIL = true;
throw e; throw e;
}) })
.catch(function(e) { .catch(function(e) {
if (e.FAIL) { if (e.FAIL) {
throw e; throw e;
} }
}) })
.then(function() { .then(function() {
// wait for the tasks the error left dangling to print their message // wait for the tasks the error left dangling to print their message
console.info('Pass: Promise rejected when expected'); console.info("Pass: Promise rejected when expected");
return promiseTimeout(1000); return promiseTimeout(1000);
}); });
}); });
} }
function promiseTimeout(timeout) { function promiseTimeout(timeout) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve) {
setTimeout(resolve, timeout); setTimeout(resolve, timeout);
}); });
} }
testBatch() testBatch()
.then(function() { .then(function() {
console.info('PROBABLY PASSED'); console.info("PROBABLY PASSED");
console.info( console.info(
'We tested what could be tested without knowing Passed what could be tested Do the results make sense?' "We tested what could be tested. Do the results make sense?"
); );
}) })
.catch(function(e) { .catch(function(e) {
console.error('FAIL!'); console.error("FAIL!");
console.error(e); console.error(e);
process.exit(500); process.exit(500);
}); });
})("undefined" !== typeof window ? window : module.exports);
}('undefined' !== typeof window ? window : module.exports));