Bluebird
相關(guān)鏈接
用bluebird實(shí)現(xiàn)更加強(qiáng)大的Promise
bluebird中常用的Promise API
官方API
bluebird與原生Promise對(duì)象及bluebird模塊的中文API文檔
Promise API
NodeJs中fs.readFile方法的基本使用方式:
const fs = require('fs'),
path = require('path');
fs.readFile(path.join(__dirname, 'sample.txt'), 'utf-8', (err, data) => {
if(err){
console.error(err);
}else{
console.log(data);
}
});
1. Promise.promisifyAll
函數(shù)Promise化是指將一個(gè)不符合promise規(guī)范的API改造成返回promise的API。Bluebird 的 Promise.promisifyAll 方法可以為一個(gè)對(duì)象的屬性中的所有方法創(chuàng)建一個(gè)對(duì)應(yīng)的使用Promise的版本。這些新創(chuàng)建方法的名稱(chēng)在已有方法的名稱(chēng)后加上"Async"后綴。
使用 Bluebird的Promise.promisifyAll來(lái)轉(zhuǎn)換上面的方法:
const Promise = require('bluebird')
fs = require('fs')
path = require('path')
Promise.promisifyAll(fs);
//新方法名后面加了‘Async’后綴, 返回一個(gè)Promise對(duì)象
fs.readFileAsync(path.join(__dirname, 'sample.txt'), 'utf-8')
.then(data => console.log(data))
.catch(err => console.error(err));
2. Promise.promisify
如果不希望把一個(gè)對(duì)象的所有方法都轉(zhuǎn)換成使用Promise方式,可以使用Promise.promisify來(lái)轉(zhuǎn)換單個(gè)方法,比如:
Promise.promisify(require("fs").readFile)
3. Promise.fromCallback
對(duì)于一個(gè) NodeJS 格式的回調(diào)方法,可以使用 Promise.fromCallback 將其轉(zhuǎn)換成一個(gè) Promise?;卣{(diào)方法的結(jié)果決定了 Promise 的狀態(tài)。
4. .spread
spread方法與then方法類(lèi)似,當(dāng)Promise的結(jié)果為數(shù)組時(shí),可以使用spread方法把數(shù)組的元素打平(flatten)為回調(diào)方法的不同參數(shù):
Promise.resolve([1,2,3])
.spread((v1, v2, v3) => console.log(v1 + v2 + v3));
5. .catch
catch 方法用來(lái)添加已回絕狀態(tài)時(shí)的回調(diào)方法,從.then()調(diào)用鏈中產(chǎn)生的任何異常都會(huì)被傳送到最近的.catch()函數(shù)中。
兩種使用方式:
捕獲所有異常
只添加回調(diào)方法,會(huì)捕獲所有的錯(cuò)誤情況
.catch(e => {
})
捕獲部分異常
使用錯(cuò)誤對(duì)象類(lèi)型或斷言來(lái)對(duì)錯(cuò)誤進(jìn)行過(guò)濾
.then(() => {
...
}).catch(TypeError, e => {
}).catch(ReferenceError, e => {
}).catch(e => {
})
6. .error
error方法和catch類(lèi)似,但是error只會(huì)處理真正的Error對(duì)象。Js中throw語(yǔ)句會(huì)拋出任何值,不一定是Error對(duì)象。throw拋出的任何值都會(huì)被catch處理,但是只有Error對(duì)象才會(huì)被error處理。
Promise.reject('not an error')
.error(err => conosle.log('wrong la'))
7. .finally
無(wú)論promise調(diào)用鏈的情況如何,.finally都會(huì)在最后被調(diào)用。最后的返回值不能在句柄中被改變。
Promise.reject(new TypeError('type error'))
.catch(TypeError, console.error)
.finally(() => console.log('done'));
catch 只會(huì)捕獲 TypeError,而 finally 中的邏輯始終會(huì)被調(diào)用
8. .bind
創(chuàng)建一個(gè)promise對(duì)象,這個(gè)對(duì)象與給定的thisArg綁定,對(duì)象內(nèi)部的this將會(huì)指向被綁定的值。這個(gè)被綁定對(duì)象所產(chǎn)生的promise對(duì)象也會(huì)被綁定到thisArg。Example
9. 狀態(tài)相關(guān)
對(duì)于一個(gè)Promise對(duì)象,可以使用如下方法查看其內(nèi)部狀態(tài):
| API | Promise狀態(tài) |
|---|---|
| isPending | 進(jìn)行中 |
| isFulfilled | 已滿(mǎn)足 |
| isPending | 已拒絕 |
| isPending | 已取消 |
| value | 獲取Promise滿(mǎn)足后的結(jié)果 |
| reason | 獲取Promise被拒后的結(jié)果 |
可以使用 cancel 方法來(lái)取消一個(gè) Promise,取消 Promise 只是表示 Promise 的回調(diào)方法不被調(diào)用,并不會(huì)自動(dòng)取消正在進(jìn)行的異步操作,取消功能默認(rèn)是禁用的,需要使用 Promise.config 來(lái)啟用該功能;如要添加自定義取消邏輯,Promise構(gòu)造方法參數(shù)中可以添加onCancel參數(shù),用來(lái)注冊(cè)Promise取消時(shí)的回調(diào)方法:
Promise.config({
cancellation: true
});
new Promise((resolve, reject, onCancel) => {
//
onCancel(() => {
//
});
}).then(console.log).cancel();
10. Promise.join
用于協(xié)調(diào)多個(gè)并行的promise。當(dāng)需要處理一個(gè)不定數(shù)量但是規(guī)格一致的多個(gè)promise時(shí),.all()是較好的選擇。但是當(dāng)我們需要協(xié)調(diào)固定數(shù)量的離散的promise實(shí)例時(shí),Promise.join()是一種更加簡(jiǎn)單(以及更加優(yōu)雅)的方法。
let Promise = require('bluebird');
Promise.join(getPic(), getComment(), getTweets(), function(pics, comments, tweets){
});
11. Promise.try
通過(guò)Promise.try啟動(dòng)一個(gè)promise鏈,并將所有同步異常封裝到promise的reject處理中。
Example
12. Promise.method
Promise.method 用來(lái)封裝一個(gè)方法,使其返回 Promise。
13. 定時(shí)器
Promise.delay 返回的 Promise 會(huì)在指定的時(shí)間之后轉(zhuǎn)換到已滿(mǎn)足狀態(tài),并使用指定的值作為結(jié)果。Promise.timeout 用來(lái)為一個(gè)已有的 Promise 添加超時(shí)功能。
Promise.delay(1000, 'hello').then(console.log);
Promise.delay(1000, 'hello')
.timeout(500, 'timed out')
.then(console.log)
.catch(console.error);
集合操作
1. Promise.all
Promise.all 接受一個(gè) Iterable 參數(shù),返回值:一個(gè)新的 Promise。結(jié)果 Promise 只有在 Iterable 中全部 Promise 對(duì)象都變?yōu)閞esolve時(shí),才會(huì)返回resolve的Promise實(shí)例;任何 Promise 出現(xiàn)錯(cuò)誤會(huì)導(dǎo)致結(jié)果 Promise 被拒絕。新的 Promise 的最終結(jié)果是一個(gè)包含 Iterable 中 Promise 的結(jié)果的數(shù)組。該 Iterable 中可以包含任何值。如果值不是 Promise,則其值會(huì)直接出現(xiàn)在結(jié)果中,并不需要等待 Promise 完成。
var files = [];
for(var i = 0; i < 10; i++){
files.push(fs.writeFileAsync('file-' + i + ‘.txt’, '', 'utf-8'));
}
Promise.all(files).then(result => {
//
})
Promise.props,類(lèi)似于.all,但是針對(duì)對(duì)象屬性的,而不是數(shù)組。
2. Promise.map
很多情況下需要把Iterable中對(duì)象轉(zhuǎn)為Promise后再等待這些Promise完成??梢允褂肞romise.map方法:
Promise.map(iterable, mapper, concurrency):
參數(shù):
- iterable: Iterable對(duì)象
- mapper: 轉(zhuǎn)化為Promise的方法
- concurrency: 可選參數(shù),控制同時(shí)運(yùn)行的Promise的數(shù)量,默認(rèn)值為Infinity,不限制并發(fā)數(shù)
//以下代碼使用map代替array.push+Promise.all方法
Promise.map(['1.txt', '2.txt', '3.txt'],
name => fs.readFileAsync(path.join(__dirname, name), 'utf-8')
).then(results => console.log(results.join(', '))).catch(console.error);
如果任何一個(gè)promise實(shí)例執(zhí)行失敗,則返回狀態(tài)為reject的Promise實(shí)例。
,即當(dāng)數(shù)組的元素對(duì)應(yīng)的promise被解決時(shí),mapper函數(shù)會(huì)盡快被調(diào)用。這意味著由最終解決組成的元素順序不一定和輸入時(shí)一樣。
Promise.mapSeries 的作用與 Promise.map 相同,只不過(guò) mapSeries 按照 Iterable 中的順序依次遍歷每個(gè)元素。
3. Promise.some
在有多個(gè) Promise 時(shí),如果只需要等待其中部分 Promise 完成,可以使用 Promise.some 并指定完成的 Promise 數(shù)量。
Promise.some([
fs.readFileAsync(path.join(__dirname, '1.txt'), 'utf-8'),
fs.readFileAsync(path.join(__dirname, '2.txt'), 'utf-8'),
fs.readFileAsync(path.join(__dirname, '3.txt'), 'utf-8')
], 2).then(results => console.log(results.join(', '))).catch(console.error);
Promise.any: 相當(dāng)于Promise.some并把數(shù)量設(shè)為1,返回值為單個(gè)值(不是長(zhǎng)度為1的數(shù)組)。
Promise.race: 與any類(lèi)似,不過(guò) race 的結(jié)果有可能是被拒絕的 Promise,因此推薦的做法是使用 any。
4. .filter和.each
Promise.filter 可以等待多個(gè) Promise 的完成,并對(duì)結(jié)果進(jìn)行過(guò)濾。它實(shí)際上的效果相當(dāng)于在 Promise.map 之后使用 Array 的 filter 方法來(lái)進(jìn)行過(guò)濾。
Promise.filter([
fs.readFileAsync('1.txt', 'utf-8'),
fs.readFileAsync('2.txt', 'utf-8'),
fs.readFileAsync('3.txt', 'utf-8')
], value => value.length > 1).then(results => console.log(results.join(', '))).catch(console.error);
.each依次遍歷一個(gè)數(shù)組或者一個(gè)數(shù)組的promise。遍歷函數(shù)會(huì)被傳入四個(gè)參數(shù):(total, item, index, arrayLength)。
如果任何一個(gè)promise實(shí)例執(zhí)行失敗,則返回狀態(tài)為reject的Promise實(shí)例。
如果遍歷函數(shù)返回了一個(gè)promise或者其他含有.then()方法的對(duì)象,那么在繼續(xù)下一個(gè)循環(huán)之前,這個(gè)promise的結(jié)果會(huì)被延遲。
官網(wǎng)例子(過(guò)濾當(dāng)前目錄下可訪(fǎng)問(wèn)的文件夾):
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");
});
5. Promise.reduce
Promise.reduce 把多個(gè) Promise 的結(jié)果縮減成單個(gè)值。其作用類(lèi)似 Array 的 reduce 方法,但是可以處理 Promise 對(duì)象。
Promise.reduce(['1.txt', '2.txt', '3.txt'],
(total, name) => {
return fs.readFileAsync(path.join(__dirname, name), 'utf-8').then(data => total + data.length);
}, 0)
.then(result => console.log(`Total size: ${result}`)).catch(console.error);
資源使用
資源釋放邏輯一般在finally中添加,但是當(dāng)Promise相互嵌套時(shí),很容易產(chǎn)生資源沒(méi)有被釋放的情況。Bluebird 提供了更加健壯的資源管理方式,即使用資源釋放器(disposer)和 Promise.using 方法。
資源釋放器以 Disposer 對(duì)象表示,通過(guò) Promise 的 disposer 方法來(lái)創(chuàng)建。創(chuàng)建時(shí)的參數(shù)是一個(gè)用來(lái)釋放資源的方法。該方法的第一個(gè)參數(shù)是資源對(duì)象,第二個(gè)參數(shù)是 Promise.using 產(chǎn)生的 Promise 對(duì)象。Disposer 對(duì)象可以傳遞給 Promise.using 來(lái)保證其資源釋放邏輯被執(zhí)行。
class Connection {
query() {
return Promise.resolve('query');
}
close() {
console.log('close');
}
}
class DB {
connect() {
return Promise.resolve(new Connection());
}
}
const disposer = new DB().connect().disposer(connection => connection.close());
Promise.using(disposer, connection => connection.query())
.then(console.log).catch(console.error);