bluebird 是一个第三方的Promise类库,并且根据Promise A+规范实现,在ECMAScript 2015的基础上添加了很多扩展方法,如.spread()展开结果集、Promise.promisifyAll()将一个模块的属性方法包装成一个Promise实例。
blueBird的API文档可以 查看这里 。
本文主要列举几个常用的API,方便在熟悉阶段的时候查用。
new Promise(Function<function resolve, Function reject> resolver)
该方法用于创建一个新的Promise实例,与Promise A+规范的实现一致。传入函数的参数有两个: resolve
、 reject
,分别用于执行成功和执行失败时的调用。
Promise A+规范中的 .then()
方法相同。
与.then()方法类似,但是上一个回调函数的结果值或者抛出的异常必须是一个数组。这个数组的元素将会依次作为参数传入fulfilledHandler处理函数。
Promise.delay(500).then(() => { return [fs.readFileAsync('file1.txt'), fs.readFileAsync('file2.txt')]; }) .spread((file1text, file2text) => { // ... });
如果想整个多个不相干但是同时进行的promise,可使用 Promise.join()
。
从.then()调用链中产生的任何异常都会被传送到最近的.catch()函数中。
这个方法有两个变体:一个用于捕获所有异常,一个用于捕获指定异常。
.catch(function(any error) handler)
.catch(class ErrorClass|function(any error)|Object predicate..., function(any error) handler)
例如:
somePromsie.then(() => { return add(); }).catch(TypeError, e => { // ... }).catch(ReferenceError, e => { // ... }).catch(e => { // ... });
类似.catch,但是它只捕获operational errors,而不是捕获某一特定类型的异常。
当一个被promise化的函数因为运行中的某些异常被终止执行,将会触发.error()。但是如果这个异常是被throws出来的,只有.catch才能捕获。
.catch(isOperationalError, e => { // ... }) 等价于: .error(e => { // ... })
无论promise调用链的情况如何,.finally都会在最后被调用。最后的返回值不能在句柄中被改变。
创建一个promise对象,这个对象与给定的thisArg绑定,对象内部的this将会指向被绑定的值。由于这个被绑定对象所产生的promise对象也会被绑定到thisArg。
用于协调多个并行的promise。当需要处理一个不定数量但是规格一致的多个promise时, .all()
是较好的选择。但是当我们需要协调固定数量的离散的promise实例时, Promise.join()
是一种更加简单(以及更加优雅)的方法。
let Promise = require('bluebird'); Promise.join( getPicture(), getComment(), getTweets(), function(pictures, comments, tweets) { // ... });
通过 Promise.try
启动一个promise链,并将所有同步异常封装到promise的reject处理中。
function getUserById(id) { return Promise.try(function() { if (typeof id !== "number") { throw new Error("id must be a number"); } return db.getUserById(id); }); }
经过 Promise.try
封装后,其同步和异步的异常都可以通过 .catch
来捕获。
包装指定的函数fn,并使包装后的函数返回一个Promise实例。
将对象包装成resolved状态的Promise实例。
创建一个状态未resolved(已解决)的promise。如果传入的值已经是一个Promise,那么它将直接返回。如果value不可以继续调用(不是thenable对象),则将会返回一个状态未fullfilled(已完成)的Promise,其带有的返回值就是value。
将对象包装成rejected状态的Promise实例。
产生一个状态为rejected(已拒绝)的promise对象,返回值带有reason。
Promise.resolue(undefined).bind(thisArg)的语法糖。
所有的集合方法在Promise对象中都有一个静态的等价方法。
将多个Promise实例包装成一个新实例。
promise数组中所有的promise实例均变为resolve时,该方法才会返回resolve的Promise实例,并将所有的结果传递到结果数组中。
如果promise数组中任何一个promise实例为reject的话,则整个 Promise.all()
调用就会立即终止,并返回一个状态未reject的新promise实例。
var files = []; for(var i = 0; i < 10; i++) { files.push(fs.writeFileAsync('file-' + i + '.txt', '', 'utf-8')); } Promise.all(files).then(function(result) { // ... });
类似.all(),但是针对对象属性的,而不是数组。
当对象属性全部变成执行通过并返回一个resolve状态的promise对象,这个promise对象的fulfillment值是一个拥有原对象键值对应关系的对象。
Promise.props({ picture: getPictures(), comments: getComments(), tweets: getTweets() }).then(result => { console.log(result.tweets, result.pictures, result.comments); });
遍历一个包含多个promise的数组,或者一个数组的promise对象。当数组中的所有元素状态均为‘已完成或者‘已拒绝’时,将会返回一个状态‘已完成’的对象。
当有一个promise组成的数组,并且希望知道他们什么时候全部处理完毕,无论最终状态都是‘已完成’还是‘已拒绝’,这个方法将很有用。
类似于.some(1)。但是终值不是一个数组,而是值本身。执行成功1个promise实例,即返回resolve状态的Promise实例。
将数组中的非Promise对象,包装成Promise对象。
给定一个数组,当其中任意一个元素状态变成‘已完成’或者‘已拒绝’时,都会立即返回一个Promise,带有终值或者reason。
成功执行指定次数后返回一个promise实例。这个promise对象的fulfillment值是一个数组,元素是执行最快的那几个promise的终值。
Promise.some([ ping('ns1.example.com'), ping('ns2.example.com'), ping('ns3.example.com'), ping('ns4.example.com') ], 2).spread((first, second) => { // ... });
如果有太多的promise的状态未‘已拒绝’,以至于最后无法完成,那么将会抛出一个AggregateError错误。
遍历一个数组或者一个数组的promise,对每一个元素使用mapper函数(参数依次为item、idnex、arrayLength)。
如果任何一个promise实例执行失败,则返回状态为reject的Promise实例。
mapper函数会尽快被调用,即当数组的元素对应的promise被解决时。这意味着由最终解决组成的元素顺序不一定和输入时一样。.map()可以用于并发协作。
let fs = Promise.promisifyAll(require('fs')); fs.readFileSync('.').map(fileName => { return JSON.parse .catch(SyntaxError, e => { e.fileName = fileName; throw e; }); });
map的静态用法:
let fs = Promise.promisifyAll(require('fs')); let fileNames = ['file1.txt', 'file2.txt']; Promise.map(fileNames, fileName => { return fs.readFileSync(fileName) .then(JSON.parse) .catch(SyntaxError, e => { e.fileName = fileName; throw e; }); });
在Options参数对象中,还可以设置concurrency属性,置顶一个并发数。并发数限制了同时执行的Promise数量。
...map(..., {concurrency: 1});
Options.concurrency的默认值为Infinity,不限制并发数。
Promise.map
还可以用于替代 数组.push + Promise.all
方法:
使用数组.push + Promise.all() var promises = []; for (var i = 0; i < fileNames.length; ++i) { promises.push(fs.readFileAsync(fileNames[i])); } Promise.all(promises).then(function() { console.log("done"); }); 使用Promise.map(): Promise.map(fileNames, function(fileName) { return fs.readFileAsync(fileName); }).then(function() { console.log("done"); });
归约一个数组或一个数组的Promise。归约函数会被传入四个参数:(total, item, index, arrayLength)。
如果归约函数返回了一个promise或者其他含有.then()方法的对象,那么在继续下一个循环之前,这个promise的结果会被延迟。
譬如,使用Promise.reduce计算从三个文件中读取值的总和,每个文件中都有一个数字10:
Promise.reduce(["file1.txt", "file2.txt", "file3.txt"], function(total, fileName) { return fs.readFileAsync(fileName, "utf8").then(function(contents) { return total + parseInt(contents, 10); }); }, 0).then(function(total) { //Total is 30 });
为指定promise数组执行一个过滤函数filterer,并返回经过筛选后promises数组。
一种让下面的过程更加有效率的方法:
var Promise = require("bluebird"); var E = require("core-error-predicates"); var fs = Promise.promisifyAll(require("fs")); fs.readdirAsync(process.cwd()).filter(function(fileName) { return fs.statAsync(fileName) .then(function(stat) { return stat.isDirectory(); }) .catch(E.FileAccessError, function() { return false; }); }).each(function(directoryName) { console.log(directoryName, " is an accessible directory"); });
依次遍历一个数组或者一个数组的promise。遍历函数会被传入四个参数:(total, item, index, arrayLength)。
如果任何一个promise实例执行失败,则返回状态为reject的Promise实例。
如果遍历函数返回了一个promise或者其他含有.then()方法的对象,那么在继续下一个循环之前,这个promise的结果会被延迟。
// Source: http://jakearchibald.com/2014/es7-async-functions/ function loadStory() { return getJSON('story.json') .then(function(story) { addHtmlToPage(story.heading); return story.chapterURLs.map(getJSON); }) .each(function(chapter) { addHtmlToPage(chapter.html); }) .then(function() { addTextToPage("All done"); }) .catch(function(err) { addTextToPage("Argh, broken: " + err.message); }) .then(function() { document.querySelector('.spinner').style.display = 'none'; }); }
Promise化是指将一个不符合promise规范的API改造成返回promise的API。
在Node.js中,通常的做法是使用 promisify
来包装非Promise对象,然后就可以使用符合promise规范的方法了。
譬如:
var fs = require('fs'); Promise.promisifyAll(fs); fs.readFileAsync("file.js", "utf8").then(...)
在fs模块中有一个例外,fs.existsAsync的行为不符合预期,因为fs.exists不会在第一个参数里回调错误。解决方法是使用fs.statAsync。
对给定的nodeFunction进行Promise化。譬如:
var readFile = Promise.promisify(require("fs").readFile); readFile("myfile.js", "utf8").then(function(contents) { return eval(contents); }).then(function(result) { console.log("The result of evaluating myfile.js", result); }).catch(SyntaxError, function(e) { console.log("File had syntax error", e); }).catch(function(e) { console.log("Error reading file", e); });
var redisGet = Promise.promisify(redisClient.get, redisClient); redisGet('foo').then(function() { //... });
当Promise化后的nodeFunction有多个返回值,应使用.spread API。
var Promise = require("bluebird"); var request = Promise.promisify(require('request')); request("http://www.google.com").spread(function(response, body) { console.log(body); }).catch(function(err) { console.error(err); });
对tartget对象的所有属性方法进行Promise化。
Promise.promisifyAll(require("redis")); //Later on, all redis client instances have promise returning functions: redisClient.hexistsAsync("myhash", "field").then(function(v) { }).catch(function(e) { });
通过使用一个节点风格的回调函数,创建一个resolved的promise。这适合那些不公开类的动态库来实现Promise化,这些不公开类的动态库不能自动使用promisifyAll来实现Promise化。
// TODO use real library that doesn't expose prototypes for promisification var object = crummyLibrary.create(); Promise.fromNode(function(callback) { object.foo("firstArgument", callback); }).then(function(result) { console.log(result); })
延时执行。
返回一个在指定时间ms后变为失败状态的promise。
检查promise是否执行成功。
检查promise是否执行失败。
检查promise是否在处理中。
检查promise是否已取消。
返回promise的执行结果。
返回promise的执行失败的原因。
Bluebird API: http://bluebirdjs.com/docs/api-reference.html