Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle timeout using request with nodejs

So, I'm making this request to a server and I set a timeout and I want to handle the timeout event, but I also want to handle the 'abort' event and differ one for another. I managed to do it with a quick fix, but I wanted to know if there is a better way to do it. The code looks like this:

makeRequest = function(json, cb){
    var requestError
    request({
        url: REQUEST_URL,
        json: json,
        timeout: 3000,
        headers: {
            'Content-Type': 'application/json'
        }
     }, function(err, res, body){
         if(err) requestError = err
         else cb(null, res, body)
     }).on('abort', function(){
         setTimeout(function({  
             if(requestError != 'ETIMEDOUT') cb(httpStatusCode.REQUEST_TIMEDOUT)
             else cb(httpStatusCode.REQUEST_ABORTED
         }, 1000)
     })
}

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. This seems like a dumb way to do it and I searched online and did not find a way yo handle only the callback event. I also noticed that the timeout triggers the on.('error', function(err){}) event, where I can handle the error, but it also calls the on.('abort', function(){}) event and I end up calling the main callback (cb) two times, crashing my application.

Is there a way I can have an event ONLY for the timeout and one ONLY for the abort, so I don't have to use setTimeout?

Or is there any property in my req object that I can check to see if that request timed out or not?

Or do you have any other suggestion to fix my problem in a less ugly way?

I'm using nodejs 0.12.2 and request 2.55.0 Thanks!

like image 734
Thiago Loddi Avatar asked Nov 01 '22 00:11

Thiago Loddi


1 Answers

One great thing about open source is that you can always just go look at the code for the module and see how it works.

If you want the error, then just listen for .on('error', function(err) {}). The error will be passed there. The .on('abort', function() {}) event does not tell you why it was aborted. But, as you can see from the relevant source code for the request module, the error event is always sent right after the abort event and it will have e.code set to ETIMEDOUT.

Here's a copy of some of the relevant source code for when .abort() is called where you can see it triggers the error event right afterwards:

  if (self.timeout && !self.timeoutTimer) {
    var timeout = self.timeout < 0 ? 0 : self.timeout
    self.timeoutTimer = setTimeout(function () {
      self.abort()
      var e = new Error('ETIMEDOUT')
      e.code = 'ETIMEDOUT'
      self.emit('error', e)
    }, timeout)

    // Set additional timeout on socket - in case if remote
    // server freeze after sending headers
    if (self.req.setTimeout) { // only works on node 0.6+
      self.req.setTimeout(timeout, function () {
        if (self.req) {
          self.req.abort()
          var e = new Error('ESOCKETTIMEDOUT')
          e.code = 'ESOCKETTIMEDOUT'
          self.emit('error', e)
        }
      })
    }
  }

So, it seems you can ignore the abort event and just listen for the error event and just call your callback on the error event.

If your code is such that it messes up if the callback is called with an error more than once (which it sounds like it is), then you can change your makeRequest() function so that it never calls the callback more than once too.

like image 157
jfriend00 Avatar answered Nov 12 '22 14:11

jfriend00