Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing timeouts for node.js callbacks

People also ask

Can we use setTimeout in NodeJS?

The setTimeout function is used to call a function after the specified number of milliseconds. The delay of the called function begins after the remaining statements in the script have finished executing. The setTimeout function is found in the Timers module of Node. js.

How does NodeJS handle timeout?

I noticed that in the timeout event both the 'abort' event is triggered and the request callback is called, in this order, so I used the setTimeout function to wait for the request callback and than handle the error in the 'abort' listener.

How many callback functions can be executed at any time?

As long as the callback code is purely sync than no two functions can execute parallel.

How is setTimeout implemented?

The setTimeout function sets a timer which executes a function on the next event cycle once the timer expires. This means, the final delay will depend on the current status of the event loop.


I'm not familiar with any libraries that do this, but it's not hard to wire up yourself.

// Setup the timeout handler
var timeoutProtect = setTimeout(function() {

  // Clear the local timer variable, indicating the timeout has been triggered.
  timeoutProtect = null;

  // Execute the callback with an error argument.
  callback({error:'async timed out'});

}, 5000);

// Call the async function
asyncFunction(arguments, function() {

  // Proceed only if the timeout handler has not yet fired.
  if (timeoutProtect) {

    // Clear the scheduled timeout handler
    clearTimeout(timeoutProtect);

    // Run the real callback.
    callback();
  }
});

You probably need to come out with a solution of your own. Like

function callBackWithATimeout (callback, timeout) {
  var run, timer;
  run = function () {
    if (timer) {
      clearTimeout(timer);
      timer = null;
      callback.apply(this, arguments);
    }
  };
  timer = setTimeout(run, timeout, "timeout");
  return run;
}

and then

asyncFunction(arguments, callBackWithATimeout(callback, 2000));

You could do something like this:

function ensureExecution(func, timeout) {
    var timer, run, called = false;

    run = function() {   
        if(!called) {
            clearTimeout(timer);
            called = true;
            func.apply(this, arguments);
        }   
    };

    timer = setTimeout(run, timeout);
    return run;
}

Usage:

asyncFunction(arguments, ensureExecution(callback, 1000));

DEMO

But note the following:

  • The timeout is started immediately when you call ensureExecution, so you cannot cache that function reference.

  • The arguments passed to the callback will differ. For example asyncFunction might pass some arguments to callback upon success, but if the function is called by the timeout, no arguments will be passed. You have to keep that it mind. You could also provide default arguments with which the function should be called in this case:

    function ensureExecution(func, timeout, args, this_obj) {
        // ...
        timer = setTimeout(function() {
            run.apply(this_obj, args);
        }, timeout);
        //...
    }
    

I ran into the same problem with a content script trying to open the port on the BG extension before the BG extension was ready. A work around was to wait for the BG extension to reply to a message and repeat this till successful. Here are the code snippets.

Content Script:

var nTimes     = 10;
var bIsReady = false;
checkBGReady();
function checkBGReady() {
  if (!bIsReady) {
    chrome.runtime.sendMessage({msgText: "hello "+nTimes}, function(response) {
      if (response && response.ack) {
        console.log("have response:"+response.ack+" "+nTimes);
        bIsReady = true;
        // continue with initialization
        bootStrap(sURL);
        checkReady();
      } else {
        console.log("have no ack response %o",response);
      }
    });
  }
  nTimes -= 1;
  if (nTimes > 0 && !bIsReady) {
    setTimeout(checkBGReady,100);
  }
}

BG Extension

  chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
    console.log(sender.tab ?"from a content script:" + sender.tab.url :"from the extension");
    if (request.msgText) {
      console.log("Have msg "+request.msgText);
       sendResponse({ack: "have contact "+request.msgText});
    }
  });

In my case it usually took after the first 100ms delay.