pax_global_header 0000666 0000000 0000000 00000000064 12265605641 0014521 g ustar 00root root 0000000 0000000 52 comment=fec51d91370233b04b75519a202cf39afe66c0e7
queue-1.0.7/ 0000775 0000000 0000000 00000000000 12265605641 0012652 5 ustar 00root root 0000000 0000000 queue-1.0.7/.gitignore 0000664 0000000 0000000 00000000027 12265605641 0014641 0 ustar 00root root 0000000 0000000 .DS_Store
node_modules
queue-1.0.7/.npmignore 0000664 0000000 0000000 00000000006 12265605641 0014645 0 ustar 00root root 0000000 0000000 test/
queue-1.0.7/LICENSE 0000664 0000000 0000000 00000002620 12265605641 0013657 0 ustar 00root root 0000000 0000000 Copyright (c) 2012, Michael Bostock
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The name Michael Bostock may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
queue-1.0.7/Makefile 0000664 0000000 0000000 00000000644 12265605641 0014316 0 ustar 00root root 0000000 0000000 all: \
queue.min.js \
component.json \
package.json
component.json: src/component.js queue.js
@rm -f $@
node src/component.js > $@
@chmod a-w $@
package.json: src/package.js queue.js
@rm -f $@
node src/package.js > $@
@chmod a-w $@
test: all
node_modules/.bin/vows
@echo
%.min.js: %.js Makefile
@rm -f $@
node_modules/.bin/uglifyjs $< -c -m -o $@
clean:
rm -f queue.min.js component.json package.json
queue-1.0.7/README.md 0000664 0000000 0000000 00000006777 12265605641 0014152 0 ustar 00root root 0000000 0000000 # queue.js
**Queue.js** is yet another asynchronous helper library for JavaScript. Think of it as a minimalist version of [Async.js](https://github.com/caolan/async) that allows fine-tuning over parallelism. Or, think of it as a version of [TameJs](http://tamejs.org/) that does not use code generation.
For example, if you wanted to stat two files in parallel:
```js
queue()
.defer(fs.stat, __dirname + "/../Makefile")
.defer(fs.stat, __dirname + "/../package.json")
.await(function(error, file1, file2) { console.log(file1, file2); });
```
Or, if you wanted to run a bazillion asynchronous tasks (here represented as an array of closures) serially:
```js
var q = queue(1);
tasks.forEach(function(t) { q.defer(t); });
q.awaitAll(function(error, results) { console.log("all done!"); });
```
Queue.js can be run inside Node.js or in a browser.
## Installation
In a browser, you can use the official hosted copy on [d3js.org](http://d3js.org):
```html
```
Queue.js supports the asynchronous module definition (AMD) API. For example, if you use [RequireJS](http://requirejs.org/), you may load as follows:
```js
require.config({paths: {queue: "http://d3js.org/queue.v1.min"}});
require(["queue"], function(queue) {
console.log(queue.version);
});
```
In Node, use [NPM](http://npmjs.org) to install:
```bash
npm install queue-async
```
And then `require("queue-async")`. (The package name is [queue-async](https://npmjs.org/package/queue-async) because the name “queue” was already taken.)
## API Reference
### queue([parallelism])
Constructs a new queue with the specified *parallelism*. If *parallelism* is not specified, the queue has infinite parallelism. Otherwise, *parallelism* is a positive integer. For example, if *parallelism* is 1, then all tasks will be run in series. If *parallelism* is 3, then at most three tasks will be allowed to proceed concurrently; this is useful, for example, when loading resources in a web browser.
### queue.defer(task[, arguments…])
Adds the specified asynchronous *task* function to the queue, with any optional *arguments*. The *task* will be called with the optional arguments and an additional callback argument; the callback should be invoked when the task has finished. Tasks can only be deferred before the *await* callback is set. If a task is deferred after the await callback is set, the behavior of the queue is undefined.
### queue.await(callback)
### queue.awaitAll(callback)
Sets the *callback* to be invoked when all deferred tasks have finished. The first argument to the *callback* is the first error that occurred, or null if no error occurred. If *await* is used, each result is passed as an additional separate argument; if *awaitAll* is used, the entire array of results is passed as the second argument to the callback. If all callbacks have already been completed by the time the *await* or *awaitAll* callback is set, the callback will be invoked immediately. This method should only be called once, after any tasks have been deferred. If the await callback is set multiple times, or set before a task is deferred, the behavior of the queue is undefined.
## Callbacks
The callbacks follow the Node.js convention where the first argument is an optional error object and the second argument is the result of the task. Queue.js does not support asynchronous functions that return multiple results; however, you can homogenize such functions by wrapping them and converting multiple results into a single object or array.
queue-1.0.7/component.json 0000664 0000000 0000000 00000000112 12265605641 0015541 0 ustar 00root root 0000000 0000000 {
"name": "queue-async",
"version": "1.0.7",
"main": "./queue.js"
}
queue-1.0.7/package.json 0000664 0000000 0000000 00000000757 12265605641 0015151 0 ustar 00root root 0000000 0000000 {
"name": "queue-async",
"version": "1.0.7",
"description": "A little helper for asynchronous JavaScript.",
"keywords": [
"asynchronous",
"async",
"queue"
],
"author": {
"name": "Mike Bostock",
"url": "http://bost.ocks.org/mike"
},
"repository": {
"type": "git",
"url": "https://github.com/mbostock/queue.git"
},
"main": "queue.js",
"devDependencies": {
"uglify-js": "2",
"vows": "0.7"
},
"scripts": {
"test": "vows; echo"
}
}
queue-1.0.7/queue.js 0000664 0000000 0000000 00000004020 12265605641 0014330 0 ustar 00root root 0000000 0000000 (function() {
var slice = [].slice;
function queue(parallelism) {
var q,
tasks = [],
started = 0, // number of tasks that have been started (and perhaps finished)
active = 0, // number of tasks currently being executed (started but not finished)
remaining = 0, // number of tasks not yet finished
popping, // inside a synchronous task callback?
error = null,
await = noop,
all;
if (!parallelism) parallelism = Infinity;
function pop() {
while (popping = started < tasks.length && active < parallelism) {
var i = started++,
t = tasks[i],
a = slice.call(t, 1);
a.push(callback(i));
++active;
t[0].apply(null, a);
}
}
function callback(i) {
return function(e, r) {
--active;
if (error != null) return;
if (e != null) {
error = e; // ignore new tasks and squelch active callbacks
started = remaining = NaN; // stop queued tasks from starting
notify();
} else {
tasks[i] = r;
if (--remaining) popping || pop();
else notify();
}
};
}
function notify() {
if (error != null) await(error);
else if (all) await(error, tasks);
else await.apply(null, [error].concat(tasks));
}
return q = {
defer: function() {
if (!error) {
tasks.push(arguments);
++remaining;
pop();
}
return q;
},
await: function(f) {
await = f;
all = false;
if (!remaining) notify();
return q;
},
awaitAll: function(f) {
await = f;
all = true;
if (!remaining) notify();
return q;
}
};
}
function noop() {}
queue.version = "1.0.7";
if (typeof define === "function" && define.amd) define(function() { return queue; });
else if (typeof module === "object" && module.exports) module.exports = queue;
else this.queue = queue;
})();
queue-1.0.7/queue.min.js 0000664 0000000 0000000 00000001264 12265605641 0015121 0 ustar 00root root 0000000 0000000 !function(){function n(n){function e(){for(;i=ap;){var u=a++,e=c[u],o=t.call(e,1);o.push(l(u)),++p,e[0].apply(null,o)}}function l(n){return function(u,t){--p,null==s&&(null!=u?(s=u,a=d=0/0,o()):(c[n]=t,--d?i||e():o()))}}function o(){null!=s?m(s):f?m(s,c):m.apply(null,[s].concat(c))}var r,i,f,c=[],a=0,p=0,d=0,s=null,m=u;return n||(n=1/0),r={defer:function(){return s||(c.push(arguments),++d,e()),r},await:function(n){return m=n,f=!1,d||o(),r},awaitAll:function(n){return m=n,f=!0,d||o(),r}}}function u(){}var t=[].slice;n.version="1.0.7","function"==typeof define&&define.amd?define(function(){return n}):"object"==typeof module&&module.exports?module.exports=n:this.queue=n}(); queue-1.0.7/src/ 0000775 0000000 0000000 00000000000 12265605641 0013441 5 ustar 00root root 0000000 0000000 queue-1.0.7/src/component.js 0000664 0000000 0000000 00000000231 12265605641 0015775 0 ustar 00root root 0000000 0000000 var queue = require("../queue");
console.log(JSON.stringify({
"name": "queue-async",
"version": queue.version,
"main": "./queue.js"
}, null, 2));
queue-1.0.7/src/package.js 0000664 0000000 0000000 00000001076 12265605641 0015376 0 ustar 00root root 0000000 0000000 var queue = require("../queue");
console.log(JSON.stringify({
"name": "queue-async",
"version": queue.version,
"description": "A little helper for asynchronous JavaScript.",
"keywords": [
"asynchronous",
"async",
"queue"
],
"author": {
"name": "Mike Bostock",
"url": "http://bost.ocks.org/mike"
},
"repository": {
"type": "git",
"url": "https://github.com/mbostock/queue.git"
},
"main": "queue.js",
"devDependencies": {
"uglify-js": "2",
"vows": "0.7"
},
"scripts": {
"test": "vows; echo"
}
}, null, 2));
queue-1.0.7/test/ 0000775 0000000 0000000 00000000000 12265605641 0013631 5 ustar 00root root 0000000 0000000 queue-1.0.7/test/queue-test.js 0000664 0000000 0000000 00000024503 12265605641 0016274 0 ustar 00root root 0000000 0000000 var queue = require("../queue"),
fs = require("fs"),
vows = require("vows"),
assert = require("assert");
var suite = vows.describe("queue");
suite.addBatch({
"version": {
"is semantic": function() {
assert.isTrue(/^([0-9]+)\.([0-9]+)\.([0-9]+)/.test(queue.version));
}
},
"example queue of fs.stat": {
topic: function() {
queue()
.defer(fs.stat, __dirname + "/../Makefile")
.defer(fs.stat, __dirname + "/../README.md")
.defer(fs.stat, __dirname + "/../package.json")
.await(this.callback);
},
"does not fail": function(error, one, two, three) {
assert.isNull(error);
},
"successfully executes the three tasks": function(error, one, two, three) {
assert.greater(one.size, 0);
assert.greater(two.size, 0);
assert.greater(three.size, 0);
}
},
"queue of single synchronous task that errors": {
topic: function() {
queue()
.defer(function(callback) { callback(-1); })
.await(this.callback);
},
"fails": function(error, result) {
assert.equal(error, -1);
assert.isUndefined(result);
}
},
"queue of single asynchronous task that errors": {
topic: function() {
queue()
.defer(function(callback) { process.nextTick(function() { callback(-1); }); })
.await(this.callback);
},
"fails": function(error, result) {
assert.equal(error, -1);
assert.isUndefined(result);
}
},
"queue with multiple tasks that error": {
topic: function() {
queue()
.defer(function(callback) { setTimeout(function() { callback(-2); }, 100); })
.defer(function(callback) { process.nextTick(function() { callback(-1); }); })
.defer(function(callback) { setTimeout(function() { callback(-3); }, 200); })
.await(this.callback);
},
"the first error is returned": function(error, one, two, three) {
assert.equal(error, -1);
assert.isUndefined(one);
assert.isUndefined(two);
assert.isUndefined(three);
}
},
"queue with multiple tasks where one errors": {
topic: function() {
queue()
.defer(function(callback) { process.nextTick(function() { callback(-1); }); })
.defer(function(callback) { process.nextTick(function() { callback(null, 'ok'); }); })
.await(this.callback);
},
"the first error is returned": function(error, one, two) {
assert.equal(error, -1);
assert.isUndefined(one);
assert.isUndefined(two);
}
},
"queue with multiple synchronous tasks that error": {
topic: function() {
queue()
.defer(function(callback) { callback(-1); })
.defer(function(callback) { callback(-2); })
.defer(function(callback) { throw new Error(); })
.await(this.callback);
},
"the first error prevents the other tasks from running": function(error, one, two, three) {
assert.equal(error, -1);
assert.isUndefined(one);
assert.isUndefined(two);
assert.isUndefined(three);
}
},
"queue of asynchronous closures, processed serially": {
topic: function() {
var tasks = [], task = asynchronousTask(), n = 10, q = queue(1);
while (--n >= 0) tasks.push(task);
tasks.forEach(function(t) { q.defer(t); });
q.awaitAll(this.callback)
},
"does not fail": function(error, results) {
assert.isNull(error);
},
"executes all tasks in series": function(error, results) {
assert.deepEqual(results, [
{active: 1, index: 0},
{active: 1, index: 1},
{active: 1, index: 2},
{active: 1, index: 3},
{active: 1, index: 4},
{active: 1, index: 5},
{active: 1, index: 6},
{active: 1, index: 7},
{active: 1, index: 8},
{active: 1, index: 9}
]);
}
},
"fully-parallel queue of ten asynchronous tasks": {
topic: function() {
var t = asynchronousTask();
queue()
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.awaitAll(this.callback);
},
"does not fail": function(error, results) {
assert.isNull(error);
},
"executes all tasks in parallel": function(error, results) {
assert.deepEqual(results, [
{active: 10, index: 0},
{active: 9, index: 1},
{active: 8, index: 2},
{active: 7, index: 3},
{active: 6, index: 4},
{active: 5, index: 5},
{active: 4, index: 6},
{active: 3, index: 7},
{active: 2, index: 8},
{active: 1, index: 9}
]);
}
},
"partly-parallel queue of ten asynchronous tasks": {
topic: function() {
var t = asynchronousTask();
queue(3)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.awaitAll(this.callback);
},
"does not fail": function(error, results) {
assert.isNull(error);
},
"executes at most three tasks in parallel": function(error, results) {
assert.deepEqual(results, [
{active: 3, index: 0},
{active: 3, index: 1},
{active: 3, index: 2},
{active: 3, index: 3},
{active: 3, index: 4},
{active: 3, index: 5},
{active: 3, index: 6},
{active: 3, index: 7},
{active: 2, index: 8},
{active: 1, index: 9}
]);
}
},
"serialized queue of ten asynchronous tasks": {
topic: function() {
var t = asynchronousTask();
queue(1)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.awaitAll(this.callback);
},
"does not fail": function(error, results) {
assert.isNull(error);
},
"executes all tasks in series": function(error, results) {
assert.deepEqual(results, [
{active: 1, index: 0},
{active: 1, index: 1},
{active: 1, index: 2},
{active: 1, index: 3},
{active: 1, index: 4},
{active: 1, index: 5},
{active: 1, index: 6},
{active: 1, index: 7},
{active: 1, index: 8},
{active: 1, index: 9}
]);
}
},
"serialized queue of ten deferred synchronous tasks": {
topic: function() {
var t = deferredSynchronousTask();
queue(1)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.awaitAll(this.callback);
t.finish();
},
"does not fail": function(error, results) {
assert.isNull(error);
},
"executes all tasks in series, within the callback of the first task": function(error, results) {
assert.deepEqual(results, [
{active: 1, index: 0},
{active: 2, index: 1},
{active: 2, index: 2},
{active: 2, index: 3},
{active: 2, index: 4},
{active: 2, index: 5},
{active: 2, index: 6},
{active: 2, index: 7},
{active: 2, index: 8},
{active: 2, index: 9}
]);
}
},
"partly-parallel queue of ten synchronous tasks": {
topic: function() {
var t = synchronousTask();
queue(3)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.awaitAll(this.callback);
},
"does not fail": function(error, results) {
assert.isNull(error);
},
"executes all tasks in series": function(error, results) {
assert.deepEqual(results, [
{active: 1, index: 0},
{active: 1, index: 1},
{active: 1, index: 2},
{active: 1, index: 3},
{active: 1, index: 4},
{active: 1, index: 5},
{active: 1, index: 6},
{active: 1, index: 7},
{active: 1, index: 8},
{active: 1, index: 9}
]);
}
},
"serialized queue of ten synchronous tasks": {
topic: function() {
var t = synchronousTask();
queue(1)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.defer(t)
.awaitAll(this.callback);
},
"does not fail": function(error, results) {
assert.isNull(error);
},
"executes all tasks in series": function(error, results) {
assert.deepEqual(results, [
{active: 1, index: 0},
{active: 1, index: 1},
{active: 1, index: 2},
{active: 1, index: 3},
{active: 1, index: 4},
{active: 1, index: 5},
{active: 1, index: 6},
{active: 1, index: 7},
{active: 1, index: 8},
{active: 1, index: 9}
]);
}
}
});
suite.export(module);
function asynchronousTask(counter) {
var active = 0;
if (!counter) counter = {scheduled: 0};
return function(callback) {
var index = counter.scheduled++;
++active;
process.nextTick(function() {
try {
callback(null, {active: active, index: index});
} finally {
--active;
}
});
};
}
function synchronousTask(counter) {
var active = 0;
if (!counter) counter = {scheduled: 0};
return function(callback) {
try {
callback(null, {active: ++active, index: counter.scheduled++});
} finally {
--active;
}
};
}
function deferredSynchronousTask(counter) {
var active = 0, deferrals = [];
if (!counter) counter = {scheduled: 0};
function task(callback) {
if (deferrals) return deferrals.push({callback: callback, index: counter.scheduled++});
try {
callback(null, {active: ++active, index: counter.scheduled++});
} finally {
--active;
}
}
task.finish = function() {
var deferrals_ = deferrals.slice();
deferrals = null;
deferrals_.forEach(function(deferral) {
try {
deferral.callback(null, {active: ++active, index: deferral.index});
} finally {
--active;
}
});
};
return task;
}