Skip to content

Latest commit

 

History

History
2814 lines (2128 loc) · 99.4 KB

API.md

File metadata and controls

2814 lines (2128 loc) · 99.4 KB

#API Reference

Note: This documentation is for bluebird 2.x and not valid for 1.x - 1.x docs here

Note that every instance promise method in the API has a static counterpart. For example Promise.map(arr, fn) is the same as calling Promise.resolve(arr).map(fn).

##Core

Core methods of Promise instances and core static methods of the Promise class.

#####new Promise(Function<Function resolve, Function reject> resolver) -> Promise

Create a new promise. The passed in function will receive functions resolve and reject as its arguments which can be called to seal the fate of the created promise.

Note: In Node.JS it is very unlikely that you will ever need to create promises yourself, see promisification

Example:

function ajaxGetAsync(url) {
    return new Promise(function (resolve, reject) {
        var xhr = new XMLHttpRequest;
        xhr.addEventListener("error", reject);
        xhr.addEventListener("load", resolve);
        xhr.open("GET", url);
        xhr.send(null);
    });
}

If you pass a promise object to the resolve function, the created promise will follow the state of that promise.


To make sure a function that returns a promise is following the implicit but critically important contract of promises, you can start a function with new Promise if you cannot start a chain immediately:

function getConnection(urlString) {
    return new Promise(function(resolve) {
        //Without new Promise, this throwing will throw an actual exception
        var params = parse(urlString);
        resolve(getAdapter(params).getConnection());
    });
}

The above ensures getConnection() fulfills the contract of a promise-returning function of never throwing a synchronous exception. Also see Promise.try and Promise.method

The resolver is called synchronously (the following is for documentation purposes and not idiomatic code):

function getPromiseResolveFn() {
    var res;
    new Promise(function (resolve) {
        res = resolve;
    });
    // res is guaranteed to be set
    return res;
}

#####.then([Function fulfilledHandler] [, Function rejectedHandler ]) -> Promise

Promises/A+ .then(). Returns a new promise chained from this promise. The new promise will be rejected or resolved depending on the passed fulfilledHandler, rejectedHandler and the state of this promise.

Example:

promptAsync("Which url to visit?").then(function(url) {
    return ajaxGetAsync(url);
}).then(function(contents) {
    alertAsync("The contents were: " + contents);
}).catch(function(e) {
    alertAsync("Exception " + e);
});

#####.spread([Function fulfilledHandler] [, Function rejectedHandler ]) -> Promise

Like calling .then, but the fulfillment value or rejection reason must be an array, which is flattened to the formal parameters of the handlers.

Promise.delay(500).then(function() {
   return [fs.readFileAsync("file1.txt"),
           fs.readFileAsync("file2.txt")] ;
}).spread(function(file1text, file2text) {
    if (file1text === file2text) {
        console.log("files are equal");
    }
    else {
        console.log("files are not equal");
    }
});

If you want to coordinate several discrete concurrent promises, use Promise.join()


#####.catch(Function handler) -> Promise

This is a catch-all exception handler, shortcut for calling .then(null, handler) on this promise. Any exception happening in a .then-chain will propagate to nearest .catch handler.

For compatibility with earlier ECMAScript version, an alias .caught() is provided for .catch().


#####.catch([Function ErrorClass|Function predicate...], Function handler) -> Promise

This extends .catch to work more like catch-clauses in languages like Java or C#. Instead of manually checking instanceof or .name === "SomeError", you may specify a number of error constructors which are eligible for this catch handler. The catch handler that is first met that has eligible constructors specified, is the one that will be called.

Example:

somePromise.then(function() {
    return a.b.c.d();
}).catch(TypeError, function(e) {
    //If a is defined, will end up here because
    //it is a type error to reference property of undefined
}).catch(ReferenceError, function(e) {
    //Will end up here if a wasn't defined at all
}).catch(function(e) {
    //Generic catch-the rest, error wasn't TypeError nor
    //ReferenceError
});

You may also add multiple filters for a catch handler:

somePromise.then(function() {
    return a.b.c.d();
}).catch(TypeError, ReferenceError, function(e) {
    //Will end up here on programmer error
}).catch(NetworkError, TimeoutError, function(e) {
    //Will end up here on expected everyday network errors
}).catch(function(e) {
    //Catch any unexpected errors
});

For a parameter to be considered a type of error that you want to filter, you need the constructor to have its .prototype property be instanceof Error.

Such a constructor can be minimally created like so:

function MyCustomError() {}
MyCustomError.prototype = Object.create(Error.prototype);

Using it:

Promise.resolve().then(function() {
    throw new MyCustomError();
}).catch(MyCustomError, function(e) {
    //will end up here now
});

However if you want stack traces and cleaner string output, then you should do:

in Node.js and other V8 environments, with support for Error.captureStackTrace

function MyCustomError(message) {
    this.message = message;
    this.name = "MyCustomError";
    Error.captureStackTrace(this, MyCustomError);
}
MyCustomError.prototype = Object.create(Error.prototype);
MyCustomError.prototype.constructor = MyCustomError;

Using CoffeeScript's class for the same:

class MyCustomError extends Error
  constructor: (@message) ->
    @name = "MyCustomError"
    Error.captureStackTrace(this, MyCustomError)

This method also supports predicate-based filters. If you pass a predicate function instead of an error constructor, the predicate will receive the error as an argument. The return result of the predicate will be used determine whether the error handler should be called.

Predicates should allow for very fine grained control over caught errors: pattern matching, error-type sets with set operations and many other techniques can be implemented on top of them.

Example of using a predicate-based filter:

var Promise = require("bluebird");
var request = Promise.promisify(require("request"));

function ClientError(e) {
    return e.code >= 400 && e.code < 500;
}

request("http://www.google.com").then(function(contents) {
    console.log(contents);
}).catch(ClientError, function(e) {
   //A client error like 400 Bad Request happened
});

For compatibility with earlier ECMAScript version, an alias .caught() is provided for .catch().


#####.error( [rejectedHandler] ) -> Promise

Like .catch but instead of catching all types of exceptions, it only catches operational errors

Note, "errors" mean errors, as in objects that are instanceof Error - not strings, numbers and so on. See a string is not an error.

It is equivalent to the following .catch pattern:

// Assumes OperationalError has been made global
function isOperationalError(e) {
    if (e == null) return false;
    return (e instanceof OperationalError) || (e.isOperational === true);
}

// Now this bit:
.catch(isOperationalError, function(e) {
    // ...
})

// Is equivalent to:

.error(function(e) {
    // ...
});

For example, if a promisified function errbacks the node-style callback with an error, that could be caught with .error(). However if the node-style callback throws an error, only .catch would catch that.

In the following example you might want to handle just the SyntaxError from JSON.parse and Filesystem errors from fs but let programmer errors bubble as unhandled rejections:

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

fs.readFileAsync("myfile.json").then(JSON.parse).then(function (json) {
    console.log("Successful json")
}).catch(SyntaxError, function (e) {
    console.error("file contains invalid json");
}).error(function (e) {
    console.error("unable to read file, because: ", e.message);
});

Now, because there is no catch-all handler, if you typed console.lag (causes an error you don't expect), you will see:

Possibly unhandled TypeError: Object #<Console> has no method 'lag'
    at application.js:8:13
From previous event:
    at Object.<anonymous> (application.js:7:4)
    at Module._compile (module.js:449:26)
    at Object.Module._extensions..js (module.js:467:10)
    at Module.load (module.js:349:32)
    at Function.Module._load (module.js:305:12)
    at Function.Module.runMain (module.js:490:10)
    at startup (node.js:121:16)
    at node.js:761:3

( If you don't get the above - you need to enable long stack traces )

And if the file contains invalid JSON:

file contains invalid json

And if the fs module causes an error like file not found:

unable to read file, because:  ENOENT, open 'not_there.txt'

#####.finally(Function handler) -> Promise

Pass a handler that will be called regardless of this promise's fate. Returns a new promise chained from this promise. There are special semantics for .finally() in that the final value cannot be modified from the handler.

Note: using .finally() for resource management is not a good idea, see resource management

Consider the example:

function anyway() {
    $("#ajax-loader-animation").hide();
}

function ajaxGetAsync(url) {
    return new Promise(function (resolve, reject) {
        var xhr = new XMLHttpRequest;
        xhr.addEventListener("error", reject);
        xhr.addEventListener("load", resolve);
        xhr.open("GET", url);
        xhr.send(null);
    }).then(anyway, anyway);
}

This example doesn't work as intended because the then handler actually swallows the exception and returns undefined for any further chainers.

The situation can be fixed with .finally:

function ajaxGetAsync(url) {
    return new Promise(function (resolve, reject) {
        var xhr = new XMLHttpRequest;
        xhr.addEventListener("error", reject);
        xhr.addEventListener("load", resolve);
        xhr.open("GET", url);
        xhr.send(null);
    }).finally(function() {
        $("#ajax-loader-animation").hide();
    });
}

Now the animation is hidden but an exception or the actual return value will automatically skip the finally and propagate to further chainers. This is more in line with the synchronous finally keyword.

For compatibility with earlier ECMAScript version, an alias .lastly() is provided for .finally().


#####.bind(dynamic thisArg) -> Promise

Create a promise that follows this promise, but is bound to the given thisArg value. A bound promise will call its handlers with the bound value set to this. Additionally promises derived from a bound promise will also be bound promises with the same thisArg binding as the original promise.

If thisArg is a promise or thenable, its resolution will be awaited for and the bound value will be the promise's fulfillment value. If thisArg rejects then the returned promise is rejected with the thisArg's rejection reason. Note that this means you cannot use this without checking inside catch handlers for promises that bind to promise because in case of rejection of thisArg, this will be undefined.


Without arrow functions that provide lexical this, the correspondence between async and sync code breaks down when writing object-oriented code. .bind() alleviates this.

Consider:

MyClass.prototype.method = function() {
    try {
        var contents = fs.readFileSync(this.file);
        var url = urlParse(contents);
        var result = this.httpGetSync(url);
        var refined = this.refine(result);
        return this.writeRefinedSync(refined);
    }
    catch (e) {
        this.error(e.stack);
    }
};

The above has a direct translation:

MyClass.prototype.method = function() {
    return fs.readFileAsync(this.file).bind(this)
    .then(function(contents) {
        var url = urlParse(contents);
        return this.httpGetAsync(url);
    }).then(function(result) {
        var refined = this.refine(result);
        return this.writeRefinedAsync(refined);
    }).catch(function(e) {
        this.error(e.stack);
    });
};

.bind() is the most efficient way of utilizing this with promises. The handler functions in the above code are not closures and can therefore even be hoisted out if needed. There is literally no overhead when propagating the bound value from one promise to another.


.bind() also has a useful side purpose - promise handlers don't need to share a function to use shared state:

somethingAsync().bind({})
.spread(function (aValue, bValue) {
    this.aValue = aValue;
    this.bValue = bValue;
    return somethingElseAsync(aValue, bValue);
})
.then(function (cValue) {
    return this.aValue + this.bValue + cValue;
});

The above without .bind() could be achieved with:

var scope = {};
somethingAsync()
.spread(function (aValue, bValue) {
    scope.aValue = aValue;
    scope.bValue = bValue;
    return somethingElseAsync(aValue, bValue);
})
.then(function (cValue) {
    return scope.aValue + scope.bValue + cValue;
});

However, there are many differences when you look closer:

  • Requires a statement so cannot be used in an expression context
  • If not there already, an additional wrapper function is required to avoid leaking or sharing scope
  • The handler functions are now closures, thus less efficient and not reusable

Note that bind is only propagated with promise transformation. If you create new promise chains inside a handler, those chains are not bound to the "upper" this:

something().bind(var1).then(function() {
    //`this` is var1 here
    return Promise.all(getStuff()).then(function(results) {
        //`this` is undefined here
        //refine results here etc
    });
}).then(function() {
    //`this` is var1 here
});

However, if you are utilizing the full bluebird API offering, you will almost never need to resort to nesting promises in the first place. The above should be written more like:

something().bind(var1).then(function() {
    //`this` is var1 here
    return getStuff();
}).map(function(result) {
    //`this` is var1 here
    //refine result here
}).then(function() {
    //`this` is var1 here
});

Also see this Stackoverflow answer on a good example on how utilizing the collection instance methods like .map() can clean up code.


If you don't want to return a bound promise to the consumers of a promise, you can rebind the chain at the end:

MyClass.prototype.method = function() {
    return fs.readFileAsync(this.file).bind(this)
    .then(function(contents) {
        var url = urlParse(contents);
        return this.httpGetAsync(url);
    }).then(function(result) {
        var refined = this.refine(result);
        return this.writeRefinedAsync(refined);
    }).catch(function(e) {
        this.error(e.stack);
    }).bind(); //The `thisArg` is implicitly undefined - I.E. the default promise `this` value
};

Rebinding can also be abused to do something gratuitous like this:

Promise.resolve("my-element")
    .bind(document)
    .then(document.getElementById)
    .bind(console)
    .then(console.log);

The above does console.log(document.getElementById("my-element"));. The .bind()s are necessary because in browser neither of the methods can be called as a stand-alone function.


#####Promise.join(Promise|Thenable|value promises..., Function handler) -> Promise

For coordinating multiple concurrent discrete promises. While .all() is good for handling a dynamically sized list of uniform promises, Promise.join is much easier (and more performant) to use when you have a fixed amount of discrete promises that you want to coordinate concurrently, for example:

var Promise = require("bluebird");
var join = Promise.join;

join(getPictures(), getComments(), getTweets(),
    function(pictures, comments, tweets) {
    console.log("in total: " + pictures.length + comments.length + tweets.length);
});
var Promise = require("bluebird");
var fs = Promise.promisifyAll(require("fs"));
var pg = Promise.promisifyAll(require("pg"));
var join = Promise.join;
var connectionString = "postgres://username:password@localhost/database";

var fContents = fs.readFileAsync("file.txt", "utf8");
var fStat = fs.statAsync("file.txt");
var fSqlClient = pg.connectAsync(connectionString).spread(function(client, done) {
    client.close = done;
    return client;
});

join(fContents, fStat, fSqlClient, function(contents, stat, sqlClient) {
    var query = "                                                              \
        INSERT INTO files (byteSize, contents)                                 \
        VALUES ($1, $2)                                                        \
    ";
   return sqlClient.queryAsync(query, [stat.size, contents]).thenReturn(query);
})
.then(function(query) {
    console.log("Successfully ran the Query: " + query);
})
.finally(function() {
    // This is why you want to use Promise.using for resource management
    if (fSqlClient.isFulfilled()) {
        fSqlClient.value().close();
    }
});

Note: In 1.x and 0.x Promise.join used to be a Promise.all that took the values in as arguments instead in an array. This behavior has been deprecated but is still supported partially - when the last argument is an immediate function value the new semantics will apply


#####Promise.try(Function fn [, Array<dynamic>|dynamic arguments] [, dynamic ctx] ) -> Promise

Start the chain of promises with Promise.try. Any synchronous exceptions will be turned into rejections on the returned promise.

function getUserById(id) {
    return Promise.try(function() {
        if (typeof id !== "number") {
            throw new Error("id must be a number");
        }
        return db.getUserById(id);
    });
}

Now if someone uses this function, they will catch all errors in their Promise .catch handlers instead of having to handle both synchronous and asynchronous exception flows.

Note about second argument: if it's specifically a true array, its values become respective arguments for the function call. Otherwise it is passed as is as the first argument for the function call.

For compatibility with earlier ECMAScript version, an alias Promise.attempt() is provided for Promise.try().


#####Promise.method(Function fn) -> Function

Returns a new function that wraps the given function fn. The new function will always return a promise that is fulfilled with the original functions return values or rejected with thrown exceptions from the original function.

This method is convenient when a function can sometimes return synchronously or throw synchronously.

Example without using Promise.method:

MyClass.prototype.method = function(input) {
    if (!this.isValid(input)) {
        return Promise.reject(new TypeError("input is not valid"));
    }

    if (this.cache(input)) {
        return Promise.resolve(this.someCachedValue);
    }

    return db.queryAsync(input).bind(this).then(function(value) {
        this.someCachedValue = value;
        return value;
    });
};

Using the same function Promise.method, there is no need to manually wrap direct return or throw values into a promise:

MyClass.prototype.method = Promise.method(function(input) {
    if (!this.isValid(input)) {
        throw new TypeError("input is not valid");
    }

    if (this.cache(input)) {
        return this.someCachedValue;
    }

    return db.queryAsync(input).bind(this).then(function(value) {
        this.someCachedValue = value;
        return value;
    });
});

#####Promise.resolve(dynamic value) -> Promise

Create a promise that is resolved with the given value. If value is already a trusted Promise, it is returned as is. If value is not a thenable, a fulfilled Promise is returned with value as its fulfillment value. If value is a thenable (Promise-like object, like those returned by jQuery's $.ajax), returns a trusted Promise that assimilates the state of the thenable.

Example: ($ is jQuery)

Promise.resolve($.get("http://www.google.com")).then(function() {
    //Returning a thenable from a handler is automatically
    //cast to a trusted Promise as per Promises/A+ specification
    return $.post("http://www.yahoo.com");
}).then(function() {

}).catch(function(e) {
    //jQuery doesn't throw real errors so use catch-all
    console.log(e.statusText);
});

#####Promise.reject(dynamic reason) -> Promise

Create a promise that is rejected with the given reason.


#####Promise.bind(dynamic thisArg) -> Promise

Sugar for Promise.resolve(undefined).bind(thisArg);. See .bind().


##Synchronous inspection

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.

Note: In recent versions of Bluebird a design choice was made to expose .reason() and .value() as well as other inspection methods on promises directly in order to make the below use case easier to work with. The Promise.settle method still returns a PromiseInspection array as its result. Every promise is now also a PromiseInspection and inspection methods can be used on promises freely.

For example, if you need to use values of earlier promises in the chain, you could nest:

// From Q Docs https://github.com/kriskowal/q/#chaining
// MIT License Copyright 2009–2014 Kristopher Michael Kowal.
function authenticate() {
    return getUsername().then(function (username) {
        return getUser(username);
    // chained because we will not need the user name in the next event
    }).then(function (user) {
        // nested because we need both user and password next
        return getPassword().then(function (password) {
            if (user.passwordHash !== hash(password)) {
                throw new Error("Can't authenticate");
            }
        });
    });
}

Or you could take advantage of the fact that if we reach password validation, then the user promise must be fulfilled:

function authenticate() {
    var user = getUsername().then(function(username) {
        return getUser(username);
    });

    return user.then(function(user) {
        return getPassword();
    }).then(function(password) {
        // Guaranteed that user promise is fulfilled, so .value() can be called here
        if (user.value().passwordHash !== hash(password)) {
            throw new Error("Can't authenticate");
        }
    });
}

In the latter the indentation stays flat no matter how many previous variables you need, whereas with the former each additional previous value would require an additional nesting level.

The PromiseInspection Interface

This interface is implemented by Promise instances as well as PromiseInspection results returned by calling Promise.settle.


#####.isFulfilled() -> boolean

See if this promise has been fulfilled.


#####.isRejected() -> boolean

See if this promise has been rejected.


#####.isPending() -> boolean

See if this promise is pending (not fulfilled or rejected).


#####.value() -> dynamic

Get the fulfillment value of this promise. Throws an error if the promise isn't fulfilled - it is a bug to call this method on an unfulfilled promise.

You should check if this promise is .isFulfilled() before calling .value() - or only call .value() in code paths where it's guaranteed that this promise is fulfilled.


#####.reason() -> dynamic

Get the rejection reason of this promise. Throws an error if the promise isn't rejected - it is a bug to call this method on an unrejected promise.

You should check if this promise is .isRejected() before calling .reason() - or only call .reason() in code paths where it's guaranteed that this promise is rejected.

##Collections

Methods of Promise instances and core static methods of the Promise class to deal with collections of promises or mixed promises and values.

All collection methods have a static equivalent on the Promise object, e.g. somePromise.map(...)... is same as Promise.map(somePromise, ...)..., somePromise.all() is same as Promise.all(somePromise) and so on.

None of the collection methods modify the original input. Holes in arrays are treated as if they were defined with the value undefined.

#####.all() -> Promise

Given an array, or a promise of an array, which contains promises (or a mix of promises and values) return a promise that is fulfilled when all the items in the array are fulfilled. The promise's fulfillment value is an array with fulfillment values at respective positions to the original array. If any promise in the array rejects, the returned promise is rejected with the rejection reason.

var files = [];
for (var i = 0; i < 100; ++i) {
    files.push(fs.writeFileAsync("file-" + i + ".txt", "", "utf-8"));
}
Promise.all(files).then(function() {
    console.log("all the files were created");
});

#####.props() -> Promise

Like .all() but for object properties instead of array items. Returns a promise that is fulfilled when all the properties of the object are fulfilled. The promise's fulfillment value is an object with fulfillment values at respective keys to the original object. If any promise in the object rejects, the returned promise is rejected with the rejection reason.

If object is a trusted Promise, then it will be treated as a promise for object rather than for its properties. All other objects are treated for their properties as is returned by Object.keys - the object's own enumerable properties.

Promise.props({
    pictures: getPictures(),
    comments: getComments(),
    tweets: getTweets()
}).then(function(result) {
    console.log(result.tweets, result.pictures, result.comments);
});
var Promise = require("bluebird");
var fs = Promise.promisifyAll(require("fs"));
var _ = require("lodash");
var path = require("path");
var util = require("util");

function directorySizeInfo(root) {
    var counts = {dirs: 0, files: 0};
    var stats = (function reader(root) {
        return fs.readdirAsync(root).map(function(fileName) {
            var filePath = path.join(root, fileName);
            return fs.statAsync(filePath).then(function(stat) {
                stat.filePath = filePath;
                if (stat.isDirectory()) {
                    counts.dirs++;
                    return reader(filePath)
                }
                counts.files++;
                return stat;
            });
        }).then(_.flatten);
    })(root).then(_);

    var smallest = stats.call("min", "size").call("pick", "size", "filePath").call("value");
    var largest = stats.call("max", "size").call("pick", "size", "filePath").call("value");
    var totalSize = stats.call("pluck", "size").call("reduce", function(a, b) {
        return a + b;
    }, 0);

    return Promise.props({
        counts: counts,
        smallest: smallest,
        largest: largest,
        totalSize: totalSize
    });
}


directorySizeInfo(process.argv[2] || ".").then(function(sizeInfo) {
    console.log(util.format("                                                \n\
        %d directories, %d files                                             \n\
        Total size: %d bytes                                                 \n\
        Smallest file: %s with %d bytes                                      \n\
        Largest file: %s with %d bytes                                       \n\
    ", sizeInfo.counts.dirs, sizeInfo.counts.files, sizeInfo.totalSize,
        sizeInfo.smallest.filePath, sizeInfo.smallest.size,
        sizeInfo.largest.filePath, sizeInfo.largest.size));
});

Note that if you have no use for the result object other than retrieving the properties, it is more convenient to use Promise.join():

Promise.join(getPictures(), getComments(), getTweets(),
    function(pictures, comments, tweets) {
    console.log(pictures, comments, tweets);
});

#####.settle() -> Promise

Given an array, or a promise of an array, which contains promises (or a mix of promises and values) return a promise that is fulfilled when all the items in the array are either fulfilled or rejected. The fulfillment value is an array of PromiseInspection instances at respective positions in relation to the input array.

This method is useful for when you have an array of promises and you'd like to know when all of them resolve - either by fulfilling or rejecting. For example:

var fs = Promise.promisifyAll(require("fs"));
// map array into array of promises
var files = ['a.txt', 'b.txt'].map(function(fileName) {
    return fs.readFileAsync(fileName, "utf8");
});
Promise.settle(files).then(function(results) {
    // results is a PromiseInspection array
    // this is reached once the operations are all done, regardless if
    // they're successful or not.
    var r = results