Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added asnyc.unwind to prevent Stack Overflow in whilst/until if used with synchronous functions #135

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
37 changes: 37 additions & 0 deletions README.md
Expand Up @@ -92,6 +92,7 @@ So far its been tested in IE6, IE7, IE8, FF3.6 and Chrome 5. Usage:
* [iterator](#iterator) * [iterator](#iterator)
* [apply](#apply) * [apply](#apply)
* [nextTick](#nextTick) * [nextTick](#nextTick)
* [unwind](#unwind)


### Utils ### Utils


Expand Down Expand Up @@ -919,6 +920,42 @@ __Example__
}); });
call_order.push('one') call_order.push('one')


---------------------------------------

<a name="unwind" />
### unwind(function, callback)

Calls a asynchronous function. If the function is not really asynchronous, the
callback is not called, but `unwind` returns the arguments as result.

This is useful to prevent stack overflows in asynchronous loop with not really
asynchronous functions.

__Arguments__

* function - A function which should be called. `task(callback)`
* callback - The callback to call if the function is really asynchronous.

__Returns__

A Array containing the arguments passed from the task to the callback.
`unwind` only returns the array if task was synchronous.
If task is asynchronous `unwind` do not return anything.

__Example__

function may(callback) {
if (Math.random() < 0.5) {
setTimeout(callback.bind(null, "async"), 1000);
} else {
callback("sync");
}
}
var sync = async.unwind(may, console.log);
if (sync) { // only if may was synchronous
console.log(sync[0]);
}



## Utils ## Utils


Expand Down
51 changes: 39 additions & 12 deletions lib/async.js
Expand Up @@ -446,6 +446,19 @@
wrapIterator(async.iterator(tasks))(); wrapIterator(async.iterator(tasks))();
}; };


async.unwind = function(task, callback) {
var result;
var cb = function(err, value) {
result = Array.prototype.slice.call(arguments, 0);
};
task(function(err, value) { cb.apply(null, arguments); });
if (!result) {
cb = callback || function () {};
} else {
return result;
}
}

async.parallel = function (tasks, callback) { async.parallel = function (tasks, callback) {
callback = callback || function () {}; callback = callback || function () {};
if (tasks.constructor === Array) { if (tasks.constructor === Array) {
Expand Down Expand Up @@ -550,31 +563,45 @@
async.concatSeries = doSeries(_concat); async.concatSeries = doSeries(_concat);


async.whilst = function (test, iterator, callback) { async.whilst = function (test, iterator, callback) {
if (test()) { while (test()) {
iterator(function (err) { function iteratorCallback(err) {
if (err) { if (err) {
return callback(err); return callback(err);
} }
async.whilst(test, iterator, callback); async.whilst(test, iterator, callback);
}); }
} var sync = async.unwind(iterator, iteratorCallback);
else { if (sync) {
callback(); if (sync[0]) {
return callback(sync[0]);
}
continue;
} else {
return;
}
} }
callback();
}; };


async.until = function (test, iterator, callback) { async.until = function (test, iterator, callback) {
if (!test()) { while (!test()) {
iterator(function (err) { function iteratorCallback(err) {
if (err) { if (err) {
return callback(err); return callback(err);
} }
async.until(test, iterator, callback); async.until(test, iterator, callback);
}); }
} var sync = async.unwind(iterator, iteratorCallback);
else { if (sync) {
callback(); if (sync[0]) {
return callback(sync[0]);
}
continue;
} else {
return;
}
} }
callback();
}; };


async.queue = function (worker, concurrency) { async.queue = function (worker, concurrency) {
Expand Down
24 changes: 24 additions & 0 deletions test/test-async.js
Expand Up @@ -1212,6 +1212,30 @@ exports['whilst'] = function (test) {
); );
}; };


exports['whilst/until sync'] = function (test) {
var i = 0, j = 0;
var testFuncWhile = function() { return i < 100000; }
var testFuncUntil = function() { return i >= 100000; }
var iter = function(c) {
i++;
j++;
c();
}
var check = function() {
test.equals(i, 100000);
test.equals(j, 100000);
test.done();
}

// This will stack overflow if we wouldn't care about it.
async.whilst(testFuncWhile, iter, function() {
test.equals(i, 100000);
test.equals(j, 100000);
i = j = 0;
async.until(testFuncUntil, iter, check);
});
};

exports['queue'] = function (test) { exports['queue'] = function (test) {
var call_order = [], var call_order = [],
delays = [160,80,240,80]; delays = [160,80,240,80];
Expand Down