bluebird与原生Promise对象及bluebird模块的中文API文档

 2016年06月15日    3987     声明


Promise对象已在ECMAScript 2015中被写入标准,且已被最新版本的浏览器和Node.js/IO.js所支持。bluebird是一个第三方Promise规范实现库,它不仅完全兼容原生Promise对象,且比原生对象功能更强大。

  1. 相关介绍
  2. 主要API

相关介绍

1 bluebirdPromise

JavaScript中有很多第三方的Promise库,在ECMAScript 2015(ES 6)中也实现了一个Promise对象,可以在最新版本的浏览器和Node.js/IO.js中使用。

bluebird同样是一个第三方Promise类库,相比其它第三方类库或标准对象来说,其有以下优点:功能更齐全而不臃肿、浏览器兼容性更好。


2 Promise对象

Promise对象已在ECMAScript 2015中做为JavaScript的标准内置对象提供,这个对象根据Promise A+规范实现,相对比较简单。除从Object对象中继承对象属性/方法外,它还有以下Promise自身的属性/方法。简单介绍如下:

Promise中的类方法

JavaScript中的类(对象)方法可以认为是静态方法(即:不需要实例化就可以使用的方法)。Promise中有以下类方法:

  • Promise.all(promiseArray) - 将多个Promise实例包装成一个新实例,多个实例会同时执行成功或失败
  • Promise.race(promiseArray) - 将多个Promise实例包装成一个新实例,实例中的任何一个实例执行成功或失败将返回
  • Promise.resolve(obj) - 将对象包装成resolve状态的Promise实例
  • Promise.reject(obj) - 将对象包装成reject状态的Promise实例

Promise中的实例方法

实例方法,是指创建Promise实例后才能使用的方法,即:被添加到原型链Promise.prototype上的方法。Promise中有以下实例方法:

  • promise.then(onFulfilled, onRejected) - 执行成功(或/与)失败的处理
  • promise.catch(onRejected) - 执行失败的处理

Promise A+规范规定:每个Promise实例中返回的都应该是一个Promise实例或thenable对象。基于这个特性,能够实现类似于同步的链式调用

Promise对象详细介绍


3 bluebird模块

bluebird是一个第三方的Promise实现,我们可通过npm命令来安装:

npm install bluebird

模块安装后,可以就可以通过require获取对模块的引用:

var Promise = require('bluebird');

bluebird模块除Promise对象中的方法外,还有很多扩展方法。如,可以通过.spread()展开结构集、可以通过Promise.promisify()方法将一个Node回调函数包装成一个Promise实例。


bluebird中主要API如下:

  1. 核心(Core)
  2. 同步检查(Synchronous inspection)
  3. 集合操作(Collections)
  4. 资源管理(Resource management)
  5. Promise包装器(Promisification)
  6. 定时器(Timers)
  7. 工具方法(Utility)


1. 核心方法(Core)

核心方法包括Promise实例和Promise对象中的核心静态方法。

1.1 new Promise - 创建实例

new Promise(function(function resolve, function reject) resolver) -> Promise

该方法是一个构造函数,用于创建一个新的Promise实例。该方法与Promise A+规范实现一致,传入的函数参数中有两个参数:resolvereject,这两个参数被封装到所创建的Promise实例中,分别用于执行成功和执行失败时的调用。

如,我们可以将XMLHttpRequest对象AJAX请求封装成一个Promise对象:

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);
  });
}

然后我就可以像下面这样进行AJAX请求:

ajaxGetAsync('http://itbilu.com')
.then(function(result){
  // 请求成功的处理
})
.catch(function(err){
  // 请求失败的处理	
})

在使用Promise进行处理时,要确保每一步都要返回一个Promise实例,并进行链式处理。如果不能立即开始一个Promise链式处理,可以使用new Promise来构造一个实例:

function getConnection(urlString) {
  return new Promise(function(resolve) {
    //不使用 new Promise时,这里会显式的抛出一个异常
    var params = parse(urlString);
    resolve(getAdapter(params).getConnection());
  });
}


1.2 .then - 实例的then()方法

.then(
  [function(any value) fulfilledHandler],
  [function(any error) rejectedHandler]
) -> Promise

Promise A+ 规范中的.then()方法,该方法可以同时传入用于操作成功后调用的fulfilledHandler回调函数和用于操作失败后调用的rejectedHandler回调函数,也可以只传入其中的一个。

参考:Promise 对象的实例方法


1.3 .spread - 展开实例返回的结果数组

.spread(
  [function(any values...) fulfilledHandler]
) -> Promise

类似调用.then方法,但执行成功后的值(fulfillment值)必须是一个数组,它会将参数扁平化并分别调用fulfilledHandler处理函数。

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");
  }
});

而使用ES 6规范进行处理时,可以像下面这样操作:

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

注意:.spread()方法会隐式的调用.all()方法,而在ES6中要显式的调用。


1.4 .catch - 实例异常处理方法

.catch是Promise链式处理中的用于异常处理的便捷方法。这个方法有两个变体:一个用于捕获所有错误,类似catch(e) {的同步方法;一个是用于捕获指定错误的变体方法。

promise中的异常捕获

function getItems(param) {
  return getItemsAsync().then(items => {
    if(!items) throw new InvalidItemsError(); 
    return items;
  }).catch(e => {
    // 从 getItemsAsync 中返回一个 rejects 或 假值时,可以从这里解决和恢复错误
    throw e; 
    // Need to rethrow unless we actually recovered, just like in the synchronous version
  }).then(process);
}

捕获所有异常

.catch(function(any error) handler) -> Promise
.caught(function(any error) handler) -> Promise

捕获所有异常是promise中.then(null, handler)的便捷方法,.then链中异常会被传递到.catch中。

为了能和ECMASript早期版本兼容,为.catch添加了一个别名方法.caught

捕获部分异常

.catch(
  class ErrorClass|function(any error)|Object predicate...,
  function(any error) handler
) -> Promise
.caught(
  class ErrorClass|function(any error)|Object predicate...,
  function(any error) handler
) -> Promise

部分捕获类似Java或C#中的catch分句,可以通过错误构造函数来进行异常的捕获处理。

somePromise.then(function() {
  return a.b.c.d();
}).catch(TypeError, function(e) {
  //If it is a TypeError, 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 was never declared at all
}).catch(function(e) {
  //Generic catch-the rest, error wasn't TypeError nor
  //ReferenceError
});

也可以在一个.catch分句中捕获多个错误:

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
});


1.5 .error - 实例操作异常处理

.error([function(any error) rejectedHandler]) -> Promise

类似.catch,但它只捕获和处理操作错误。

.catch处理相比:

// 假定会全局触发一个 OperationalError 异常
function isOperationalError(e) {
    if (e == null) return false;
    return (e instanceof OperationalError) || (e.isOperational === true);
}

// catch 捕获
.catch(isOperationalError, function(e) {
    // ...
})

// 等价于:
.error(function(e) {
    // ...
});

如,我们可能希望捕获JSON.parse引发的SyntaxError错误和fs文件操作相关错误,可以像下面这样传递处理错误:

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);
});


1.6 .finally - 实例最终调用方法

.finally(function() handler) -> Promise
.lastly(function() handler) -> Promise

这个方法不会考虑Promise实例的执行状态,而始终会调用并返回一个新promise链。

考虑如下一个处理:

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);
}

在上在操作中,我们期望的操作是无论执行结果如果,都要将元素隐藏,这样我们需要在.then链的两种执行状态中分别传入隐藏方法。

这种情况非常适合使用.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();
  });
}


1.7 Promise.join - 关联多个不相关的实例

Promise.join(
  Promise<any>|any values...,
  function handler
) -> Promise

关联多个不相关的Promise实例。.all很适合处理统一处理动态大小的列表,而.join使处理多个离散的Promise实例处理更加容易。

在这个方法,方法的最后一个参数是一个回调函数,其中包含所有的处理成功的结果:

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);
});


1.8 Promise.try - 包装为Promise实例

Promise.try(function() fn) -> Promise
Promise.attempt(function() fn) -> Promise

通过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来捕获。


1.9 Promise.method - 将函数包装为返回Promise实例的函数

Promise.method(function(...arguments) fn) -> function

包装指定的函数fn,包装后函数会返回一个Promise实例。

如,对于如下一个不使用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;
  });
};

通过Promise.method对包装以方法时:

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;
  });
});


1.10 Promise.resolve - 包装值为Promiseresolved实例

Promise.resolve(Promise<any>|any value) -> Promise

通过指定值创建Promiseresolved实例。如果值已使用Promise,那会返回它。如果值不是thenable对象,会将其包装成一个会执行成功的Promise实例,该值将会做为fulfillment值返回。

Promise.resolve($.get("http://www.google.com")).then(function() {
  // 从处理函数中返回一个thenable,按Promises/A+ 规范自动载换为可信的Promise 
  return $.post("http://www.yahoo.com");
}).then(function() {

}).catch(function(e) {
  //jQuery 不会抛出真的错误,所以使用 catch-all
  console.log(e.statusText);
});


1.11 Promise.reject - 包装值为Promisereject实例

Promise.reject(any error) -> Promise

将指定的值包装为一个rejected状态的Promise实例


2. 同步检查(Synchronous inspection)

同步检查相关API,是指用于Promise实例检查的一些方法。

2.1 PromiseInspection接口

interface PromiseInspection {
  any reason()
  any value()
  boolean isPending()
  boolean isRejected()
  boolean isFulfilled()
  boolean isCancelled()
}

这个接口会被Promise实例实现,PromiseInspection结果通过.reflect()方法提供。


2.2 isFulfilled - 检查是否执行成功

.isFulfilled() -> boolean

检查promise是否执行成功


2.3 isRejected - 检查是否执行失败

.isRejected() -> boolean

检查promise是否执行失败


2.4 isPending - 检查是否处理中

.isPending() -> boolean

检查promise是否处理中(非fulfilled、rejected或cancelled)


2.5 isCancelled - 检查是否已取消

.isCancelled() -> boolean

检查promise是否已取消


2.6 value - 执行结果

.value() -> any

返回promise的执行结果,需要通过.isFulfilled()来判断已执行成功


2.7 reason - 执行失败原因

.reason() -> any

返回promise的执行失败的原因,需要通过.isRejected()来判断已执行失败


3. 集合操作(Collections)

3.1 Promise.all - 执行多个Promise实例

Promise.all(Iterable<any>|Promise<Iterable<any>> input) -> Promise

等待多个Promise实例执行完成,

参数是一个Iterable(可迭代)对象(如:promise数组)。在这个方法中,promise数组中所有的promise实例都变为resolve的时候,该方法才会返回,并将所有结果传递到结果数组中。promise数组中任何一个promise为reject的话,则整个Promise.all调用会立即终止,并返回一个状态为reject的新的promise对象。

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");
});


3.2 Promise.props - 对象属性包装成Promise实例

Promise.props(Object|Map|Promise<Object|Map> input) -> Promise

Promise.props功能与Promise.all功能类型,不同的是访方法用于对象属性或Map实体而不是可迭代值,即把对象或Map实体的函数属性键变为Promise实例。对象属性或Map实体全部执行通过后返回一个promise,这个promise对象的fulfillment值是一个拥有原对象或Map键值的对象或Map

Promise.props({
  pictures: getPictures(),
  comments: getComments(),
  tweets: getTweets()
}).then(function(result) {
  console.log(result.tweets, result.pictures, result.comments);
});


3.3 Promise.any - 执行成1个即返回

Promise.any(Iterable<any>|Promise<Iterable<any>> input) -> Promise

类似Promise.any,但成功数为1。promise执行成功后,实现值不是数组而1个直接值。


3.4 Promise.some - 成功指定次数后返回

Promise.some(
  Iterable<any>|Promise<Iterable<any>> input,
  int count
 ) -> Promise

指定的Iterable数组或Iterablepromise,执行成功指定次数count后即返回,实现值是一个数组

如,ping 4个网址,返回最快的2个:

Promise.some([
  ping("ns1.example.com"),
  ping("ns2.example.com"),
  ping("ns3.example.com"),
  ping("ns4.example.com")
], 2).spread(function(first, second) {
  console.log(first, second);
});


3.5 Promise.map - Map操作

Promise.map(
  Iterable<any>|Promise<Iterable<,
  function(any item, int index, int length) mapper,
  [Object {concurrency: int=Infinity} options]
 ) -> Promise

为指定的Iterable数组或Iterablepromise,执行一个处理函数mapper并返回执行后的数组或Iterablepromise。

Promises会等待mapper全部执行完成后返回,如果数组中的promise执行全部分成功则Promises中是执行成功值。如果任何一个promise执行失败,Promises对应的也是拒绝值。

Promise.map可以用于替数组.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) {
    // Promise.map 等待操作成功后返回
    return fs.readFileAsync(fileName);
}).then(function() {
    console.log("done");
});


3.6 Promise.reduce - Reduce操作

Promise.reduce(
  Iterable<any>|Promise<Iterable<,
  function(any accumulator, any item, int index, int length) reducer,
  [any initialValue]
) -> Promise

为指定的Iterable数组或Iterablepromise,执行一个处理函数reducer,并返回一个经过Reduce处理后promise。

reducer函数会最终返回一个promise。数组中的所有promise处理成功后,会返回一个成功状态的promise,任何一个执行失败都会返回一个拒绝状态的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
});


3.7 Promise.filter - filter过滤

Promise.filter(
  Iterable<any>|Promise<Iterable<,
  function(any item, int index, int length) filterer,
  [Object {concurrency: int=Infinity} options]
) -> Promise

为指定的Iterable数组或Iterablepromise,执行一个过滤函数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");
});


3.8 Promise.each - 依次执行

Promise.each(
  Iterable<any>|Promise<Iterable>,
  function(any item, int index, int length) iterator
) -> Promise

为指定的Iterable数组或Iterablepromise,执行一个函数iterator,该函数参数为(value, index, length)value输入数组中promise的resolved值。

iterator函数会最终返回一个promise。数组中的所有promise处理成功后,会返回一个成功状态的promise,任何一个执行失败都会返回一个拒绝状态的promise。


3.9 Promise.mapSeries - 顺序执行的Map操作

Promise.mapSeries(
  Iterable<any>|Promise<Iterable<,
  function(any item, int index, int length) mapper
) -> Promise

Promise.map,但会按数组顺序依次执行mapper


3.10 Promise.race - 非Promise对象包成Promise对象

Promise.race(Iterable<any>|Promise<Iterable<) -> Promise

将数组中的非Promise对象,包装成Promise对象。参见ES 6Promise.race方法


.all - 实例all方法

.all() -> Promise

相当于Promise.all(this)


3.12 .props - 实例props方法

.props() -> Promise

相当于Promise.props(this)


3.13 .any - 实例any方法

.any() -> Promise

相当于Promise.any(this)


3.14 .some - 实例some方法

.some(int count) -> Promise

相当于Promise.some(this, count)


3.15 .map - 实例map方法

.map(
  function(any item, int index, int length) mapper,
  [Object {concurrency: int=Infinity} options]
) -> Promise

相当于Promise.map(this, mapper, options)


3.16 .filter - 实例filter方法

.filter(
  function(any item, int index, int length) filterer,
  [Object {concurrency: int=Infinity} options]
) -> Promise

相当于Promise.filter(this, filterer, options)


3.17 .each - 实例each方法

.each(function(any item, int index, int length) iterator) -> Promise

相当于Promise.each(this, iterator)


3.18 .mapSeries - 实例mapSeries方法

.mapSeries(function(any item, int index, int length) mapper) -> Promise

相当于Promise.mapSeries(this, iterator)


4. 资源管理(Resource management)

在异步操作中,合理的使用和优化资源是非常有难度的。如,对于如下一个操作:

function doStuff() {
  return Promise.all([
    connectionPool.getConnectionAsync(),
    fs.readFileAsync("file.sql", "utf8")
  ]).spread(function(connection, fileContents) {
    return connection.query(fileContents).finally(function() {
      connection.close();
    });
  }).then(function() {
    console.log("query successful and connection closed");
  });
}

随着操作时间和操作资源的增加,spread可能会耗尽服务器资源,这样就失去了异步编程的优势。

bluebird提供了以下两个方法,让我们合理的优化和释放资源:

  • .disposer - 为对象资源提供一个包装和释放方法
  • Promise.using - 使用资源,并会自动调用资源的disposer释放方法的函数
var using = Promise.using;

using(
  getConnection(),
  fs.readFileAsync("file.sql", "utf8"), function(connection, fileContents) {
    return connection.query(fileContents);
}).then(function() {
  console.log("query successful and connection closed");
});


4.1 .disposer - 添加资源释放器

.disposer(function(any resource, Promise usingOutcomePromise) disposer) -> Disposer

用于资源释放器方法的扩展方法,这个方法将在调用Promise.using时清除资源。

返回一个Disposer对象,该对象封装了资源清理方法。用户可以通过Promise.using获取资源或对资源进行清理。第二个参数是传给资源处理器的promise对象,可以用于同步检查资源。

如:

// 会返回一个promise但是一个Disposer
function getConnection() {
  return db.connect().disposer(function(connection, promise) {
    connection.close();
  });
}

在上面示例中能过getConnection()方法返回的连接,只能通过Promise.using使用,如:

function useConnection(query) {
  return Promise.using(getConnection(), function(connection) {
    return connection.sendQuery(query).then(function(results) {
      return process(results);
    })
  });
}


4.2 promise.using - 使用资源

Promise.using(
  Promise|Disposer|any resource,
  Promise|Disposer|any resource...,
  function(any resources...) handler
) -> Promise
Promise.using(
  Array<Promise|Disposer|Any> resources,
  function(Array<any> resources) handler
) -> Promise

结合.disposer,确保无论任何情况,指定的资源释放器都会在promise回调时调用。

如,可以像下面这样使用多个资源:

using(getConnection(), function(conn1) {
  return using(getConnection(), function(conn2) {
    // use conn1 and conn 2 here
  });
}).then(function() {
  // Both connections closed by now
})


5. Promise包装器(Promisification)

官方文档叫做Promisification,意思是将一个没有Promise的API对象包装成有Promise的API对象,即将其Promise化,或者可以理解成Promise包装器。

bluebird中有用于单个函数包装的Promise.promisify方法,也有用于对象属性包装的Promise.promisifyAll方法。

5.1 Promise.promisify - 单个函数对象的Promise

Promise.promisify(
  function(any arguments..., function callback) nodeFunction,
  [Object {
    multiArgs: boolean=false,
    context: any=this
  } options]
) -> function

将Node.js对象nodeFunction包装为Promise对象,包装后将使用.then.catchPromise方法代替Node.js中的回调。

如,我们可以像下面这样将Node.jsfs模块中的readFile方法包装成一个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);
});


5.2 Promise.promisifyAll - 对象属性的Promise

Promise.promisifyAll(
  Object target,
  [Object {
    suffix: String="Async",
    multiArgs: boolean=false,
    filter: boolean function(String name, function func, Object target, boolean passesDefaultFilter),
    promisifier: function(function originalFunction, function defaultPromisifier)
  } options]
) -> Object

将传入的对象实体的属性包装成Promise对象。

如,我们可以fs模块中的所有方法都Promise化:

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

fs.readFileAsync("myfile.js", "utf8").then(function(contents) {
    console.log(contents);
}).catch(function(e) {
    console.error(e.stack);
});


5.3 Promise.fromCallback - 包装为成功状态的Promise

Promise.fromCallback(
  function(function callback) resolver,
  [Object {multiArgs: boolean=false} options]
) -> Promise
Promise.fromNode(
  function(function callback) resolver,
  [Object {multiArgs: boolean=false} options]
) -> Promise

从Node.js的callback回调函数中返回一个resolved状态的Promise实例。

使用示例:

var Promise = require("bluebird");
// "email-templates" doesn't expose prototypes for promisification
var emailTemplates = Promise.promisify(require('email-templates'));
var templatesDir = path.join(__dirname, 'templates');

emailTemplates(templatesDir).then(function(template) {
  return Promise.fromCallback(function(callback) {
    return template("newsletter", callback);
  }, {multiArgs: true}).spread(function(html, text) {
    console.log(html, text);
  });
});

也可以用于Function.prototype.bind

var Promise = require("bluebird");
// "email-templates" doesn't expose prototypes for promisification
var emailTemplates = Promise.promisify(require('email-templates'));
var templatesDir = path.join(__dirname, 'templates');

emailTemplates(templatesDir).then(function(template) {
  return Promise.fromCallback(template.bind(null, "newsletter"), {multiArgs: true})
    .spread(function(html, text) {
      console.log(html, text);
    });
});


5.4 Promise.asCallback - Promise对象的callback

.asCallback(
  [function(any error, any value) callback],
  [Object {spread: boolean=false} options]
) -> this
.nodeify(
  [function(any error, any value) callback],
  [Object {spread: boolean=false} options]
) -> this

Promise实例注册Node.js式的callback回调

如,对于如下一个Promise实例注册为Node.js的回调式:

function getDataFor(input, callback) {
  return dataFromDataBase(input).asCallback(callback);
}

我们会像这样使用这个Promise实例:

getDataFor("me").then(function(dataForMe) {
  console.log(dataForMe);
});

但将其回调化后,就可以像下面这样使用:

getDataFor("me", function(err, dataForMe) {
  if( err ) {
    console.error( err );
  }
  console.log(dataForMe);
});


6. 定时器

用于promise延时和超时的方法


6.1 Promise.delay - 延时执行

Promise.delay(
  int ms,
  [any|Promise<any> value=undefined]
) -> Promise

将指定的Promise对象value延时ms毫秒后执行

Promise.delay(500).then(function() {
  console.log("500 ms passed");
  return "Hello world";
}).delay(500).then(function(helloWorldString) {
  console.log(helloWorldString);
  console.log("another 500 ms passed") ;
});


6.2 .delay - 实例延时执行

.delay(int ms) -> Promise

相当于Promise.delay(ms, this)


6.3 .timeout - 实例超时

.timeout(
  int ms,
  [String message="operation timed out"]
) -> Promise
.timeout(
  int ms,
  [Error error]
) -> Promise

返回一个在指定时间ms后变为失败状态的promise。

var Promise = require("bluebird");
var fs = Promise.promisifyAll(require('fs'));
fs.readFileAsync("huge-file.txt").timeout(100).then(function(fileContents) {
  // 执行成功的一些操作
}).catch(Promise.TimeoutError, function(e) {
  console.log("could not read file within 100ms");
});


7. 工具方法(utility)

可能会用到的一些工具方法

7.1 .tap - 非失败状态调用

.tap(function(any value) handler) -> Promise

类似.finally,但只有在非失败状态被调用

getUser().tap(function(user) {
  //Like in finally, if you return a promise from the handler
  //the promise is awaited for before passing the original value through
  return recordStatsAsync();
}).then(function(user) {
  //user is the user from getUser(), not recordStatsAsync()
});


7.2 .call - call方法

.call(
  String methodName,
  [any args...]
)

一个便捷的call方法。

promise.then(function(obj) {
  return obj[methodName].call(obj, arg...);
});

如,将some做为Array对象的内置方法:

var Promise = require("bluebird");
var fs = Promise.promisifyAll(require("fs"));
var path = require("path");
var thisPath = process.argv[2] || ".";
var now = Date.now();

fs.readdirAsync(thisPath)
  .map(function(fileName) {
    return fs.statAsync(path.join(thisPath, fileName));
  })
  .call("some", function(stat) {
    return (now - new Date(stat.mtime)) < 10000;
  })
  .then(function(someFilesHaveBeenModifiedLessThanTenSecondsAgo) {
    console.log(someFilesHaveBeenModifiedLessThanTenSecondsAgo);
});


7.3 .get - 获取属性值

便捷的属性访问器方法

promise.then(function(obj) {
  return obj[propertyName];
});


7.4 .return - 返回一个值

.return(any value) -> Promise
.thenReturn(any value) -> Promise

便捷的属性访问器方法

.then(function() {
 return value;
});

如,可以像下面这样返回执行成功后的值:

function getData() {
  var data;

  return query().then(function(result) {
    data = result;
  }).return(data);
}


7.5 .throw - 抛出异常

.throw(any reason) -> Promise
.thenThrow(any reason) -> Promise

可以像下面这样使用:

.then(function() {
 throw reason;
});


7.6 .catchReturn - 捕获异常并返回一个值

.catchReturn(
  [class ErrorClass|function(any error) predicate],
  any value
) -> Promise

使用方法如下:

.catch(function() {
 return value;
});


7.7 .catchThrow - 捕获并抛出异常

.catchThrow(
  [class ErrorClass|function(any error) predicate],
  any reason
) -> Promise

使用方法如下:

.catch(function() {
  throw reason;
});


7.8 .reflect - 返回总是成功状态的promise

.reflect() -> Promise<PromiseInspection>

使用方法如下:

总是返回一个成功状态的promise实例

var object = {
  first: getPromise1(),
  second: getPromise2()
};
Promise.props(Object.keys(object).reduce(function(newObject, key) {
  newObject[key] = object[key].reflect();
  return newObject;
}, {})).then(function(object) {
  if (object.first.isFulfilled()) {
    console.log("first was fulfilled with", object.first.value());
  } else {
    console.error("first was rejected with", object.first.reason());
  }
})


7.9 Promise.noConflict - 解决浏览器环境的命名冲突

Promise.noConflict() -> Object

用于解决览器环境的命名空间冲突

使用方法如下:

<!-- 另一个 promise 库首先被加载 -->
<script type="text/javascript" src="/scripts/other_promise.js"></script>
<script type="text/javascript" src="/scripts/bluebird_debug.js"></script>
<script type="text/javascript">
//Release control right after
var Bluebird = Promise.noConflict();

// 从另一个promise库中返回 Bluebird命名空间:
var promise = Bluebird.resolve(new Promise());
</script>


7.10 Promise.noConflict - 调度设置

Promise.setScheduler(function(function fn) scheduler) -> function

设置一个在异步调度中使用的函数。

Promise.setScheduler(function(fn) {
  setTimeout(fn, 0);
});