This article is partially or completely unfinished. You are welcome to create pull requests to help completing this article.

Synchronous inspection

Synchronous inspection allows you to retrieve the fulfillment value of an already fulfilled promise or the rejection reason of an already rejected promise synchronously.

Often it is known in certain code paths that a promise is guaranteed to be fulfilled at that point - it would then be extremely inconvenient to use .then to get at the promise's value as the callback is always called asynchronously.

See the API on synchronous inspection for more information.

Concurrency coordination

Through the use of .each and .map doing things just at the right concurrency level becomes a breeze.

Promisification on steroids

Promisification means converting an existing promise-unaware API to a promise-returning API.

The usual way to use promises in node is to Promise.promisifyAll some API and start exclusively calling promise returning versions of the APIs methods. E.g.

var
 fs
 =
 require
(
"fs"
);

Promise
.
promisifyAll
(
fs
);

// Now you can use fs as if it was designed to use bluebird promises from the beginning


fs
.
readFileAsync
(
"file.js"
,
 "utf8"
).
then
(...)

Note that the above is an exceptional case because fs is a singleton instance. Most libraries can be promisified by requiring the library's classes (constructor functions) and calling promisifyAll on the .prototype . This only needs to be done once in the entire application's lifetime and after that you may use the library's methods exactly as they are documented, except by appending the "Async" -suffix to method calls and using the promise interface instead of the callback interface.

As a notable exception in fs , fs.existsAsync doesn't work as expected, because Node's fs.exists doesn't call back with error as first argument. More at #418 . One possible workaround is using fs.statAsync .

Some examples of the above practice applied to some popular libraries:

// The most popular redis module

var
 Promise
 =
 require
(
"bluebird"
);

Promise
.
promisifyAll
(
require
(
"redis"
));

// The most popular mongodb module

var
 Promise
 =
 require
(
"bluebird"
);

Promise
.
promisifyAll
(
require
(
"mongodb"
));

// The most popular mysql module

var
 Promise
 =
 require
(
"bluebird"
);

// Note that the library's classes are not properties of the main export

// so we require and promisifyAll them manually

Promise
.
promisifyAll
(
require
(
"mysql/lib/Connection"
).
prototype
);

Promise
.
promisifyAll
(
require
(
"mysql/lib/Pool"
).
prototype
);

// Mongoose

var
 Promise
 =
 require
(
"bluebird"
);

Promise
.
promisifyAll
(
require
(
"mongoose"
));

// Request

var
 Promise
 =
 require
(
"bluebird"
);

Promise
.
promisifyAll
(
require
(
"request"
));

// Use request.getAsync(...) not request(..), it will not return a promise

// mkdir

var
 Promise
 =
 require
(
"bluebird"
);

Promise
.
promisifyAll
(
require
(
"mkdirp"
));

// Use mkdirp.mkdirpAsync not mkdirp(..), it will not return a promise

// winston

var
 Promise
 =
 require
(
"bluebird"
);

Promise
.
promisifyAll
(
require
(
"winston"
));

// rimraf

var
 Promise
 =
 require
(
"bluebird"
);

// The module isn't promisified but the function returned is

var
 rimrafAsync
 =
 Promise
.
promisify
(
require
(
"rimraf"
));

// xml2js

var
 Promise
 =
 require
(
"bluebird"
);

Promise
.
promisifyAll
(
require
(
"xml2js"
));

// jsdom

var
 Promise
 =
 require
(
"bluebird"
);

Promise
.
promisifyAll
(
require
(
"jsdom"
));

// fs-extra

var
 Promise
 =
 require
(
"bluebird"
);

Promise
.
promisifyAll
(
require
(
"fs-extra"
));

// prompt

var
 Promise
 =
 require
(
"bluebird"
);

Promise
.
promisifyAll
(
require
(
"prompt"
));

// Nodemailer

var
 Promise
 =
 require
(
"bluebird"
);

Promise
.
promisifyAll
(
require
(
"nodemailer"
));

// ncp

var
 Promise
 =
 require
(
"bluebird"
);

Promise
.
promisifyAll
(
require
(
"ncp"
));

// pg

var
 Promise
 =
 require
(
"bluebird"
);

Promise
.
promisifyAll
(
require
(
"pg"
));

In all of the above cases the library made its classes available in one way or another. If this is not the case, you can still promisify by creating a throwaway instance:

var
 ParanoidLib
 =
 require
(
"..."
);

var
 throwAwayInstance
 =
 ParanoidLib
.
createInstance
();

Promise
.
promisifyAll
(
Object
.
getPrototypeOf
(
throwAwayInstance
));

// Like before, from this point on, all new instances + even the throwAwayInstance suddenly support promises

See also Promise.promisifyAll .

Debuggability and error handling

Surfacing unhandled errors

The default approach of bluebird is to immediately log the stack trace when there is an unhandled rejection. This is similar to how uncaught exceptions cause the stack trace to be logged so that you have something to work with when something is not working as expected.

However because it is possible to handle a rejected promise at any time in the indeterminate future, some programming patterns will result in false positives. Because such programming patterns are not necessary and can always be refactored to never cause false positives, we recommend doing that to keep debugging as easy as possible . You may however feel differently so bluebird provides hooks to implement more complex failure policies.

Such policies could include:

  • Logging after the promise became GCd (requires a native node.js module)
  • Showing a live list of rejected promises
  • Using no hooks and using .done to manually to mark end points where rejections will not be handled
  • Swallowing all errors (challenge your debugging skills)
  • ...

See global rejection events to learn more about the hooks.

Long stack traces

Normally stack traces don't go beyond asynchronous boundaries so their utility is greatly reduced in asynchronous code:

setTimeout
(
function
()
 {

    setTimeout
(
function
()
 {

        setTimeout
(
function
()
 {

            a
.
b
.
c
;

        },
 1
);

    },
 1
)

},
 1
)

ReferenceError: a is not defined
    at null._onTimeout file.js:4:13
    at Timer.listOnTimeout (timers.js:90:15)

Of course you could use hacks like monkey patching or domains but these break down when something can't be monkey patched or new apis are introduced.

Since in bluebird promisification is made trivial, you can get long stack traces all the time:

var
 Promise
 =
 require
(
"bluebird"
);


Promise
.
delay
(
1
)

    .
delay
(
1
)

    .
delay
(
1
).
then
(
function
()
 {

        a
.
b
.
c
;

    });

Unhandled rejection ReferenceError: a is not defined
    at file.js:6:9
    at processImmediate [as _immediateCallback] (timers.js:321:17)
From previous event:
    at Object.<anonymous> (file.js:5:15)
    at Module._compile (module.js:446:26)
    at Object.Module._extensions..js (module.js:464:10)
    at Module.load (module.js:341:32)
    at Function.Module._load (module.js:296:12)
    at Function.Module.runMain (module.js:487:10)
    at startup (node.js:111:16)
    at node.js:799:3

And there is more. Bluebird's long stack traces additionally eliminate cycles, don't leak memory, are not limited to a certain amount of asynchronous boundaries and are fast enough for most applications to be used in production. All these are non-trivial problems that haunt straight-forward long stack trace implementations.

See installation on how to enable long stack traces in your environment.

Error pattern matching

Perhaps the greatest thing about promises is that it unifies all error handling into one mechanism where errors propagate automatically and have to be explicitly ignored.

Warnings

Promises can have a steep learning curve and it doesn't help that promise standards go out of their way to make it even harder. Bluebird works around the limitations by providing warnings where the standards disallow throwing errors when incorrect usage is detected. See Warning Explanations for the possible warnings that bluebird covers.

See installation on how to enable warnings in your environment.

Note - in order to get full stack traces with warnings in Node 6.x+ you need to enable to --trace-warnings flag which will give you a full stack trace of where the warning is coming from.

Promise monitoring

This feature enables subscription to promise lifecycle events via standard global events mechanisms in browsers and Node.js.

The following lifecycle events are available:

  • "promiseCreated" - Fired when a promise is created through the constructor.
  • "promiseChained" - Fired when a promise is created through chaining (e.g. .then ).
  • "promiseFulfilled" - Fired when a promise is fulfilled.
  • "promiseRejected" - Fired when a promise is rejected.
  • "promiseResolved" - Fired when a promise adopts another's state.
  • "promiseCancelled" - Fired when a promise is cancelled.

This feature has to be explicitly enabled by calling Promise.config with monitoring: true .

The actual subscription API depends on the environment.

1. In Node.js, use process.on :

// Note the event name is in camelCase, as per Node.js convention.

process
.
on
(
"promiseChained"
,
 function
(
promise
,
 child
)
 {

    // promise - The parent promise the child was chained from

    // child - The created child promise.

});

2. In modern browsers use window.addEventListener (window context) or self.addEventListener() (web worker or window context) method:

// Note the event names are in mashedtogetherlowercase, as per DOM convention.

self
.
addEventListener
(
"promisechained"
,
 function
(
event
)
 {

    // event.details.promise - The parent promise the child was chained from

    // event.details.child - The created child promise.

});

3. In legacy browsers use window.oneventname = handlerFunction; .

// Note the event names are in mashedtogetherlowercase, as per legacy convention.

window
.
onpromisechained
 =
 function
(
promise
,
 child
)
 {

    // event.details.promise - The parent promise the child was chained from

    // event.details.child - The created child promise.

};

Resource management

Cancellation and timeouts

See Cancellation for how to use cancellation.

// Enable cancellation

Promise
.
config
({
cancellation
:
 true
});


var
 fs
 =
 Promise
.
promisifyAll
(
require
(
"fs"
));


// In 2000ms or less, load & parse a file 'config.json'

var
 p
 =
 Promise
.
resolve
(
'./config.json'
)

 .
timeout
(
2000
)

 .
catch
(
console
.
error
.
bind
(
console
,
 'Failed to load config!'
))

 .
then
(
fs
.
readFileAsync
)

 .
then
(
JSON
.
parse
);

// Listen for exception event to trigger promise cancellation

process
.
on
(
'unhandledException'
,
 function
(
event
)
 {

 // cancel config loading

 p
.
cancel
();

});

Scoped prototypes

Building a library that depends on bluebird? You should know about the "scoped prototype" feature.

If your library needs to do something obtrusive like adding or modifying methods on the Promise prototype, uses long stack traces or uses a custom unhandled rejection handler then... that's totally ok as long as you don't use require("bluebird") . Instead you should create a file that creates an isolated copy. For example, creating a file called bluebird-extended.js that contains:

                //NOTE the function call right after

module
.
exports
 =
 require
(
"bluebird/js/main/promise"
)();

Your library can then use var Promise = require("bluebird-extended"); and do whatever it wants with it. Then if the application or other library uses their own bluebird promises they will all play well together because of Promises/A+ thenable assimilation magic.

Async/Await