Skip to content

New feature: setting time limits #1007

@vote539

Description

@vote539

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions