⚠️ Warning: this is an old article and may include information that’s out of date. ⚠️

A while back I had a need for a general timeout option for async.parallel and was surprised I couldn’t find much information about how to do it. I was using async.parallel to make a lot of REST calls in parallel, but one of them was getting blocked by a firewall, resulting in an eventual timeout minutes later.

One option was to specify a shorter timeout for each individual REST call, including the one causing the problem. But I wanted a simpler solution: a general timeout for that specific async.parallel instance that made it clear all tasks should finish within a certain period of time. Otherwise return a timeout as an error, allowing the program to continue (and retry if necessary).

It was somewhat surprising there wasn’t an option built into async.parallel, as I could imagine other folks must have had the same problem at some point in time.

In any case, I wrote a pretty simple wrapper function that gets the job done (see example usage below):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
var async = require('async');

// async.parallel with optional timeout (options.timeoutMS)
function parallel(options, tasks, cb) {
    //  sanity checks
    options = options || {};

    // no timeout wrapper; passthrough to async.parallel
    if(typeof options.timeoutMS != 'number') return async.parallel(tasks, cb);

    var timeout = setTimeout(function(){
        // remove timeout, so we'll know we already erred out
        timeout = null;

        // error out
        cb('async.parallel timed out out after ' + options.timeoutMS + 'ms.', null);
    }, options.timeoutMS);

    async.parallel(tasks, function(err, result){
        // after all tasks are complete

        // noop if timeout was called and annulled
        if(!timeout) return;

        // cancel timeout (if timeout was set longer, and all parallel tasks finished sooner)
        clearTimeout(timeout);

        // passthrough the data to the cb
        cb(err, result);
    });
}


// example usage
parallel({timeoutMS: 10000}, [  // 10 second timeout
    function(){ ... },
    function(){ ... }
],
function(err, results) {
    if(err) {
        // timeouts can now be handled here
    }
});


// an example forcing a timeout to occur
parallel({
    timeoutMS: 1000   // 1 second timeout
},
[
    function(done){
        // task 1 completes in 100ms
        setTimeout(function(){
            done(null, 'foo');
        }, 100);
    },
    function(done){
        // task 2 completes in 2000ms, forcing a timeout error
        setTimeout(function(){
            done(null, 'bar');
        }, 2000);
    }
],
function(err, results) {
    // err = 'async.parallel timed out out after 1000ms.'
});