pax_global_header00006660000000000000000000000064122656056410014521gustar00rootroot0000000000000052 comment=fec51d91370233b04b75519a202cf39afe66c0e7 queue-1.0.7/000077500000000000000000000000001226560564100126525ustar00rootroot00000000000000queue-1.0.7/.gitignore000066400000000000000000000000271226560564100146410ustar00rootroot00000000000000.DS_Store node_modules queue-1.0.7/.npmignore000066400000000000000000000000061226560564100146450ustar00rootroot00000000000000test/ queue-1.0.7/LICENSE000066400000000000000000000026201226560564100136570ustar00rootroot00000000000000Copyright (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/Makefile000066400000000000000000000006441226560564100143160ustar00rootroot00000000000000all: \ 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.md000066400000000000000000000067771226560564100141520ustar00rootroot00000000000000# 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.json000066400000000000000000000001121226560564100155410ustar00rootroot00000000000000{ "name": "queue-async", "version": "1.0.7", "main": "./queue.js" } queue-1.0.7/package.json000066400000000000000000000007571226560564100151510ustar00rootroot00000000000000{ "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.js000066400000000000000000000040201226560564100143300ustar00rootroot00000000000000(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.js000066400000000000000000000012641226560564100151210ustar00rootroot00000000000000!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/000077500000000000000000000000001226560564100134415ustar00rootroot00000000000000queue-1.0.7/src/component.js000066400000000000000000000002311226560564100157750ustar00rootroot00000000000000var queue = require("../queue"); console.log(JSON.stringify({ "name": "queue-async", "version": queue.version, "main": "./queue.js" }, null, 2)); queue-1.0.7/src/package.js000066400000000000000000000010761226560564100153760ustar00rootroot00000000000000var 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/000077500000000000000000000000001226560564100136315ustar00rootroot00000000000000queue-1.0.7/test/queue-test.js000066400000000000000000000245031226560564100162740ustar00rootroot00000000000000var 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; }