Skip to content

Commit

Permalink
Avoid recursion during tail calls.
Browse files Browse the repository at this point in the history
An asynchronous task that finishes would previously trigger recursive invocation
of subsequent synchronous tasks. Now these tasks are handled in a loop instead.
  • Loading branch information
mbostock committed Mar 31, 2013
1 parent fde1de6 commit 7bd5059
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 31 deletions.
9 changes: 3 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
NODE_PATH ?= ./node_modules
JS_COMPILER = $(NODE_PATH)/uglify-js/bin/uglifyjs
JS_TESTER = $(NODE_PATH)/vows/bin/vows

all: \
queue.min.js \
component.json \
Expand All @@ -18,11 +14,12 @@ package.json: src/package.js queue.js
@chmod a-w $@

test: all
@$(JS_TESTER)
node_modules/.bin/vows
@echo

%.min.js: %.js Makefile
@rm -f $@
$(JS_COMPILER) < $< > $@
node_modules/.bin/uglifyjs $< -c -m -o $@

clean:
rm -f queue.min.js component.json package.json
2 changes: 1 addition & 1 deletion component.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "queue-async",
"version": "1.0.1",
"version": "1.0.2",
"main": "./queue.js"
}
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "queue-async",
"version": "1.0.1",
"version": "1.0.2",
"description": "A little helper for asynchronous JavaScript.",
"keywords": [
"asynchronous",
Expand All @@ -13,14 +13,14 @@
},
"repository": {
"type": "git",
"url": "http://github.com/mbostock/queue.git"
"url": "https://github.com/mbostock/queue.git"
},
"main": "queue.js",
"devDependencies": {
"uglify-js": "2.2.3",
"vows": "0.7.0"
"uglify-js": "~2.2.5",
"vows": "~0.7.0"
},
"scripts": {
"test": "./node_modules/vows/bin/vows"
"test": "node_modules/.bin/vows"
}
}
20 changes: 11 additions & 9 deletions queue.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
(function() {
if (typeof module === "undefined") self.queue = queue;
else module.exports = queue;
queue.version = "1.0.2";

queue.version = "1.0.1";
var slice = [].slice;

function queue(parallelism) {
var queue = {},
Expand All @@ -14,13 +15,13 @@
await = noop,
awaitAll;

if (arguments.length < 1) parallelism = Infinity;
if (!parallelism) parallelism = Infinity;

queue.defer = function() {
if (!error) {
var node = arguments;
node.index = results.push(undefined) - 1;
if (tail) tail.next = node, tail = tail.next;
node.i = results.push(undefined) - 1;
if (tail) tail._ = node, tail = tail._;
else head = tail = node;
++remaining;
pop();
Expand All @@ -43,13 +44,14 @@
};

function pop() {
if (head && active < parallelism) {
var popping;
while (popping = head && active < parallelism) {
var node = head,
f = node[0],
a = Array.prototype.slice.call(node, 1),
i = node.index;
a = slice.call(node, 1),
i = node.i;
if (head === tail) head = tail = null;
else head = head.next;
else head = head._;
++active;
a.push(function(e, r) {
--active;
Expand All @@ -63,7 +65,7 @@
notify();
} else {
results[i] = r;
if (--remaining) pop();
if (--remaining) popping || pop();
else notify();
}
});
Expand Down
2 changes: 1 addition & 1 deletion queue.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions src/package.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ console.log(JSON.stringify({
},
"repository": {
"type": "git",
"url": "http://github.com/mbostock/queue.git"
"url": "https://github.com/mbostock/queue.git"
},
"main": "queue.js",
"devDependencies": {
"uglify-js": "2.2.3",
"vows": "0.7.0"
"uglify-js": "~2.2.5",
"vows": "~0.7.0"
},
"scripts": {
"test": "./node_modules/vows/bin/vows"
"test": "node_modules/.bin/vows"
}
}, null, 2));
39 changes: 34 additions & 5 deletions test/queue-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,10 +188,10 @@ suite.addBatch({
}
},

"fully-parallel queue of ten synchronous tasks": {
"serialized queue of ten deferred synchronous tasks": {
topic: function() {
var t = synchronousTask();
queue()
var t = deferredSynchronousTask();
queue(1)
.defer(t)
.defer(t)
.defer(t)
Expand All @@ -203,12 +203,13 @@ suite.addBatch({
.defer(t)
.defer(t)
.awaitAll(this.callback);
t.finish();
},
"does not fail": function(error, results) {
assert.isNull(error);
},
"executes all tasks in series": function(error, results) {
assert.deepEqual(results, [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
"executes all tasks in series, within the callback of the first task": function(error, results) {
assert.deepEqual(results, [1, 2, 2, 2, 2, 2, 2, 2, 2, 2]);
}
},

Expand Down Expand Up @@ -288,3 +289,31 @@ function synchronousTask() {
}
};
}

function deferredSynchronousTask() {
var active = 0,
callbacks = [];

function task(callback) {
if (callbacks) return callbacks.push(callback);
try {
callback(null, ++active);
} finally {
--active;
}
}

task.finish = function() {
var callbacks_ = callbacks.slice();
callbacks = null;
callbacks_.forEach(function(callback) {
try {
callback(null, ++active);
} finally {
--active;
}
});
};

return task;
}

0 comments on commit 7bd5059

Please sign in to comment.