Permalink
Browse files

Avoid recursion during tail calls.

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...
1 parent fde1de6 commit 7bd5059bb2c0a94c0ca21d87b346f59bf7b31d02 @mbostock mbostock committed Mar 31, 2013
Showing with 59 additions and 31 deletions.
  1. +3 −6 Makefile
  2. +1 −1 component.json
  3. +5 −5 package.json
  4. +11 −9 queue.js
  5. +1 −1 queue.min.js
  6. +4 −4 src/package.js
  7. +34 −5 test/queue-test.js
View
@@ -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 \
@@ -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
View
@@ -1,5 +1,5 @@
{
"name": "queue-async",
- "version": "1.0.1",
+ "version": "1.0.2",
"main": "./queue.js"
}
View
@@ -1,6 +1,6 @@
{
"name": "queue-async",
- "version": "1.0.1",
+ "version": "1.0.2",
"description": "A little helper for asynchronous JavaScript.",
"keywords": [
"asynchronous",
@@ -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"
}
}
View
@@ -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 = {},
@@ -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();
@@ -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;
@@ -63,7 +65,7 @@
notify();
} else {
results[i] = r;
- if (--remaining) pop();
+ if (--remaining) popping || pop();
else notify();
}
});
View
@@ -1 +1 @@
-(function(){if(typeof module==="undefined")self.queue=queue;else module.exports=queue;queue.version="1.0.1";function queue(parallelism){var queue={},active=0,remaining=0,head,tail,error=null,results=[],await=noop,awaitAll;if(arguments.length<1)parallelism=Infinity;queue.defer=function(){if(!error){var node=arguments;node.index=results.push(undefined)-1;if(tail)tail.next=node,tail=tail.next;else head=tail=node;++remaining;pop()}return queue};queue.await=function(f){await=f;awaitAll=false;if(!remaining)notify();return queue};queue.awaitAll=function(f){await=f;awaitAll=true;if(!remaining)notify();return queue};function pop(){if(head&&active<parallelism){var node=head,f=node[0],a=Array.prototype.slice.call(node,1),i=node.index;if(head===tail)head=tail=null;else head=head.next;++active;a.push(function(e,r){--active;if(error!=null)return;if(e!=null){error=e;remaining=results=head=tail=null;notify()}else{results[i]=r;if(--remaining)pop();else notify()}});f.apply(null,a)}}function notify(){if(error!=null)await(error);else if(awaitAll)await(null,results);else await.apply(null,[null].concat(results))}return queue}function noop(){}})();
+(function(){function n(n){function t(){for(var u;u=i&&n>a;){var o=i,f=o[0],v=l.call(o,1),d=o.i;i=i===r?r=null:i._,++a,v.push(function(n,l){--a,null==p&&(null!=n?(p=n,c=s=i=r=null,e()):(s[d]=l,--c?u||t():e()))}),f.apply(null,v)}}function e(){null!=p?v(p):o?v(null,s):v.apply(null,[null].concat(s))}var i,r,o,f={},a=0,c=0,p=null,s=[],v=u;return n||(n=1/0),f.defer=function(){if(!p){var n=arguments;n.i=s.push(void 0)-1,r?(r._=n,r=r._):i=r=n,++c,t()}return f},f.await=function(n){return v=n,o=!1,c||e(),f},f.awaitAll=function(n){return v=n,o=!0,c||e(),f},f}function u(){}"undefined"==typeof module?self.queue=n:module.exports=n,n.version="1.0.2";var l=[].slice})();
View
@@ -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));
View
@@ -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)
@@ -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]);
}
},
@@ -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.