Promises: All The Wrong Ways by Kyle Simpson
View OriginalNormalise your
Normalise your promise
Many different promise libraries and implementations out there. You need to normalise them before using them:
Promise.resolve(foo()).then(nextStep)…
This will normalise
foo()
If
nextStep
is also using a different type of promise implementation, the then(..)
step will normalise it as well.Timing
Promise.resolve().then(firstStep)
will put it into the end of the event-loop tick. Does not put it to a next tike like setTimeout
does, but after all the other synchronous code has been executed.To normalise error handling:
var p;
try {
p = Promise.resolve( firstStep() );
}
catch (err) {
p = Promise.reject( err );
}
p
.then( .. )
..
Scopes
consider:
function getOrderDetails(orderID) {
var _order;
return db.find( "orders", orderID )
.then( function(order){
_order = order;
return db.find( "customers", order.customerID )
} )
.then( function(customer){
_order.customer = customer;
return _order;
} );
}
This is a very bad pattern. Uses side-effects in the form of _order
to share a value between the then(..)
handlers.Instead you have to nest the
then(..)
handlers:
function getOrderDetails(orderID) {
return db.find( "orders", orderID )
.then( function(order){
return db.find( "customers", order.customerID )
// nested then() instead of flattened to outer chain,
// so that we still have lexical scope access to `order`
.then( function(customer){
order.customer = customer;
return order;
} );
} );
}
Flow control with Promises
Promises are not designed for async flow control. They hold (are a placeholder for) a future value.
For async flow control use generators with promises (or the
async.. await
) pattern (with a runner utility function – to make sure they run to the end):
async function main() {
await firstStep();
await secondStep();
await thirdStep();
}
// or:
function *main() {
yield firstStep();
yield secondStep();
yield thirdStep();
}
A more complete example with returned values:
async function main() {
try {
var val1 = await firstStep();
var val2 = await secondStep( val1 );
var val3 = await thirdStep( val1, val2 );
console.log( "Final: ", val3 );
}
catch (err) {
console.error( err );
}
}
Summary
Calling
then(..)
on a promise is a code smell and anti-pattern.
Don't use promises and then(..)
for flow control!