Nodejs - How to promise http.request? refused twice

I am trying to wrap http.request in Promise :

  new Promise(function(resolve, reject) { var req = http.request({ host: '127.0.0.1', port: 4000, method: 'GET', path: '/api/v1/service' }, function(res) { if (res.statusCode < 200 || res.statusCode >= 300) { // First reject reject(new Error('statusCode=' + res.statusCode)); return; } var body = []; res.on('data', function(chunk) { body.push(chunk); }); res.on('end', function() { try { body = JSON.parse(Buffer.concat(body).toString()); } catch(e) { reject(e); return; } resolve(body); }); }); req.on('error', function(err) { // Second reject reject(err); }); req.write('test'); }).then(function(data) { console.log(data); }).catch(function(err) { console.log(err); }); 

If I get an error statusCode from a remote server, it will call First reject and after a while the Second reject . How to make it cause only one failure (I think the first failure is correct in this case)? I think I need to close res myself, but there is no close() method for the ClientResponse object.

UPD: The second deviation is very rare - why?

+8
source share
2 answers

Your code is almost perfect. To retell a bit, you need a function that wraps http.request with this form:

 function httpRequest(params, postData) { return new Promise(function(resolve, reject) { var req = http.request(params, function(res) { // on bad status, reject // on response data, cumulate it // on end, parse and resolve }); // on request error, reject // if there post data, write it to the request // important: end the request req.end() }); } 

Note the addition of params and postData so that it can be used as a general purpose request. And note that the last line of req.end() - which should always be called - was missing from the OP code.

Applying these pair changes to the OP code ...

 function httpRequest(params, postData) { return new Promise(function(resolve, reject) { var req = http.request(params, function(res) { // reject on bad status if (res.statusCode < 200 || res.statusCode >= 300) { return reject(new Error('statusCode=' + res.statusCode)); } // cumulate data var body = []; res.on('data', function(chunk) { body.push(chunk); }); // resolve on end res.on('end', function() { try { body = JSON.parse(Buffer.concat(body).toString()); } catch(e) { reject(e); } resolve(body); }); }); // reject on request error req.on('error', function(err) { // This is not a "Second reject", just a different sort of failure reject(err); }); if (postData) { req.write(postData); } // IMPORTANT req.end(); }); } 

This is not tested, but it should work fine ...

 var params = { host: '127.0.0.1', port: 4000, method: 'GET', path: '/api/v1/service' }; // this is a get, so there no post data httpRequest(params).then(function(body) { console.log(body); }); 

And these promises can also be chained ...

 httpRequest(params).then(function(body) { console.log(body); return httpRequest(otherParams); }).then(function(body) { console.log(body); // and so on }); 
+19
source

It’s easier for you to use the bluebird api, you can promisify the request module and use the async request function as a promise itself, or you have the option to use the request-promise module, which forces you not to work on creating a promise, but to use objects that already encapsulate the module using promise, here is an example:

 var rp = require('request-promise'); rp({host: '127.0.0.1', port: 4000, method: 'GET', path: '/api/v1/service'}) .then(function (parsedBody) { // GET succeeded... }) .catch(function (err) { // GET failed... }); 
-one
source

All Articles