Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

nodejs - How to promisify http.request? reject got called two times

I'm trying to wrap http.request into 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 recieve errornous statusCode from remote server it will call First reject and after a bit of time Second reject. How to make properly so it calls only single reject (I think First reject is proper one in this case)? I think I need to close res myself, but there is no close() method on ClientResponse object.

UPD: Second reject triggers very rarely - why?

like image 818
happy_marmoset Avatar asked Jul 22 '16 18:07

happy_marmoset


People also ask

What happens if resolve () method is called twice inside the promise executor function?

The only thing to understand is that once resolved (or rejected), that is it for a defered object - it is done. If you call then(...) on its promise again, you immediately get the (first) resolved/rejected result. Additional calls to resolve() will not have any effect.

Can a promise be awaited multiple times?

The implication of this is that promises can be used to memoize async computations. If you consume a promise whose result will be needed again later: consider holding on to the promise instead of its result! It's fine to await a promise twice, if you're happy to yield twice.

How many times can a promise be rejected?

')); }); An important point to note: A Promise executor should call only one resolve or one reject . Once one state is changed (pending => fulfilled or pending => rejected), that's all.

What does Promisify mean?

promisify() method basically takes a function as an input that follows the common Node. js callback style, i.e., with a (err, value) and returns a version of the same that returns a promise instead of a callback.


1 Answers

Your code is almost fine. To restate a little, you want 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's post data, write it to the request         // important: end the request req.end()     }); } 

Notice the addition of params and postData so this can be used as a general purpose request. And notice the last line req.end() -- which must always be called -- was missing from the OP code.

Applying those couple 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 untested, 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's no post data  httpRequest(params).then(function(body) {     console.log(body); }); 

And these promises can be chained, too...

httpRequest(params).then(function(body) {     console.log(body);     return httpRequest(otherParams); }).then(function(body) {     console.log(body);     // and so on }); 
like image 148
danh Avatar answered Oct 04 '22 05:10

danh