-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Description
In my application, I needed an API that let me set a "time limit" on an asynchronous function. If the asynchronous function does not call its callback within the time limit, then the callback should be called with some default arguments.
A real-life use case would be, for example, loading content from a URL. Maybe you want to wait only 30 seconds for the content to return, and return some error if the content hasn't loaded in time.
Here's what I came up with:
async.timeLimit = function(milliseconds, defaults, callback) {
var calledNext = false;
var timer = null;
var normalCallback = function() {
if (!calledNext) callback.apply(null, arguments);
clearTimeout(timer);
};
var timeoutCallback = function() {
callback.apply(null, defaults);
calledNext = true;
};
timer = setTimeout(timeoutCallback, milliseconds);
return normalCallback;
};
Here's how you use it:
function callback(err) {
if (err) console.log(err);
else console.log("Hello World");
}
// No time limit:
setTimeout(
callback,
3000);
// Normal callback wins:
setTimeout(
async.timeLimit(5000, [new Error("Out of time")], callback),
3000);
// Time limit wins:
setTimeout(
async.timeLimit(1000, [new Error("Out of time")], callback),
3000);
So in general, anywhere you would normally pass a callback as an argument, you can wrap that callback in async.timeLimit
.
One possible issue with the above implementation is that the garbage collector will not be able to remove the reference to callback
until the normal callback has been called, since the function normalCallback
retains a reference to callback
, and normalCallback
is retained by the asynchronous function that hasn't finished yet. I came up with an alternate implementation that might be friendlier on the GC: when the timeout callback is resolved, the normal callback is replaced by a noop. I haven't run any profiles to test whether or not this is an improvement.
async.timeLimit = function(milliseconds, defaults, callback) {
var timer, normalCallbackRef;
var normalCallback = function() {
callback.apply(null, arguments);
clearTimeout(timer);
};
var timeoutCallback = function() {
callback.apply(null, defaults);
normalCallbackRef = function(){}; // noop
};
timer = setTimeout(timeoutCallback, milliseconds);
normalCallbackRef = normalCallback;
return function() {
normalCallbackRef.apply(null, arguments);
};
};
Any thoughts on async.timeLimit
? I can submit a PR if you want.