Doing this with native Promises
. It's good to understand the guts.
This here is known as the "Promise Constructor Antipattern" as pointed out by @Bergi in the comments. Don't do this. Check out the better method below.
var contextA = new Promise(function(resolve, reject) {
request('http://someurl.com', function(err, response, body) {
if(err) reject(err);
else {
resolve(body.toJSON());
}
});
});
var contextB = new Promise(function(resolve, reject) {
request('http://contextB.com', function(err, response, contextB) {
if(err) reject(err);
else {
contextA.then(function(contextA) {
res.render('page', contextA, contextB);
});
}
});
});
The nifty trick here, and I think by using raw promises you come to appreciate this, is that contextA
resolves once and then we have access to it's resolved result. This is, we never make the above request to someurl.com
, but still have access to contextA
's JSON.
So I can conceivable create a contextC
and reuse the JSON without having to make another request. Promises
always only resolve once. You would have to take that anonymous executor function out and put it in a new Promise
to refresh that data.
Bonus note:
This executes contextA
and contextB
in parallel, but will do the final computation that needs both contexts when both A
& B
are resolved.
Here's my new stab at this.
The main problem with the above solution is none of the promises are reusable and they are not chained which is a key feature of Promises.
However, I still recommend promisifying
your request
library yourself and abstaining from adding another dependency to your project. Another benefit of promisifying
yourself is you can write your own rejection
logic. This is important if you're working with a particular API
that sends error
messages in the body. Let's take a look:
//Function that returns a new Promise. Beats out constructor anti-pattern.
const asyncReq = function(options) {
return new Promise(function (resolve, reject) {
request(options, function(err, response, body) {
//Rejected promises can be dealt with in a `catch` block.
if(err) {
return reject(err);
}
//custom error handling logic for your application.
else if (hasError(body)) {
return reject(toError(body));
}
// typically I just `resolve` `res` since it contains `body`.
return resolve(res);
}
});
};
asyncReq(urlA)
.then(function(resA) {
//Promise.all is the preferred method for managing nested context.
return Promise.all([resA, asyncReq(urlB)]);
})
.then(function(resAB) {
return render('page', resAB[0], resAB[1]);
})
.catch(function(e) {
console.err(e);
});