Page Contents

Promises are an alternative for asynchronous operations that can be simpler to write and understand than traditional callback-based approaches. Promises also enable you to handle asynchronous errors with something similar to a synchronous try/catch block.

REVIEW COMMENT from Rand

See #1409, #1575 and #418.

Add explanation of why you might want to use Promises in LB, e.g. remote method

What is a promise?

A promise represents the result of an asynchronous operation. A promise is in one of the following states:

  • pending - The initial state of a promise (neither fulfilled nor rejected).

  • fulfilled - The action relating to the promise succeeded. When a successful promise is fulfilled, all of the pending callbacks are called with the value. If more callbacks are registered in the future, they will be called with the same value. Fulfillment is the asynchronous analog for returning a value.

  • rejected - The action relating to the promise failed. When a promise is rejected it invokes the errbacks that are waiting and remembers the error that was rejected for future errbacks that are attached. Rejection is the asynchronous analog for throwing an exception.

  • settled - The promise has been fulfilled or rejected. Once a promise is settled, it is immutable (it can never change again).

Additional terminology:

  • Callback: A function executed if a a promise is fulfilled with a value.
  • Errback: A function executed if a promise is rejected, with an exception.
  • Progressback: A function executed to show that progress has been made toward resolution of a promise.

For more general information on promises, see:

LoopBack support for promises

Promise support in LoopBack is still in progress. The following are complete:

See LoopBack issue #418 for details.

Setup

When using Node v0.12- or io.js 1.0-, you can use the native global Promise object.

With earlier versions of Node, use Bluebird. When running in an environment that supports native promises, Bluebird will automatically “fall back” to use them, so typically, it’s easier to set up Bluebird so you don’t need to be concerned with platform support. Simply, enter this command to update your application’s dependencies in package.json:

$ npm install -save bluebird

Then, in your code:

var Promise = require('bluebird');
...

Using promises in LoopBack

For example, here is how you would call a CRUD operation on a model that extends PersistedModel with standard callbacks:

MyModel.find(function(err, result){
  ...
  if (err) cb(err)
})

With promises, you would instead do the following:

MyModel.find()
.then(function(result){
  ... // Called if the operation succeeds.
})
.catch(function(err){
  ... // Called if the operation encounters an error.
})

Another example:

var Promise = require('bluebird');
CoffeeShop.status = function() {
  var currentDate = new Date();
  var currentHour = currentDate.getHours();
  var OPEN_HOUR = 6;
  var CLOSE_HOUR = 20;
  console.log('Current hour is ' - currentHour);
  var response;
  if (currentHour > OPEN_HOUR && currentHour < CLOSE_HOUR) {
    response = 'We are open for business.';
  } else {
    response = 'Sorry, we are closed. Open daily from 6am to 8pm.';
  }

  return Promise.resolve(response);
};

Promises can simplify, for example, defining asynchronous remote methods. Instead of:

common/models/my-model.js

module.exports = function(MyModel) {
 MyModel.myFunc = function(input, cb) {
   Todo.find(function(err, data) {
     if(err) return cb(err);
     cb(null, generateStats(input, data));
   });
 };

With promises, this is reduced to:

common/models/my-model.js

MyModel.myFunc = function(input, cb) {
   return Todo.find()
     .map(generateStats(input));
 };
 MyModel.remoteMethod('myFunc', ...);
}

DAO and relation methods

BelongsTo and HasOne relations return promises by calling the get() method on the relation property:

MyModel.relation.get()
  .then(function(result) {
    // ...
  });

HasMany relations return promises by calling the find() method on the relation property:

MyModel.relations.find()
  .then(function(results) {
    // ...
  });