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?
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.
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.
')); }); 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.
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.
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 });
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With