Promises: All The Wrong Ways by Kyle Simpson

In notebook:
Article Notes
Created at:
2016-06-08
Updated:
2016-06-08
Tags:
JavaScript Fundamentals
View Original

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!