參考鏈接:
學習RxJS: 導入
http://www.moye.me/2016/05/31/learning_rxjs_part_one_preliminary/
RxJS中文文檔
https://cn.rx.js.org
Lodash中文文檔
https://www.lodashjs.com
阮一峰的ES6入門:Promise 對象
http://es6.ruanyifeng.com/#docs/promise
ES6關于Promise的用法
https://segmentfault.com/a/1190000011652907
ES6 Promise 用法(我見過最簡潔優(yōu)秀的文章)
https://blog.csdn.net/shan1991fei/article/details/78966297
引子
新手們在異步編程里跌倒時,永遠會有這么一個經(jīng)典問題:
怎么在一次異步調(diào)用里return一個結(jié)果???
老司機說要用【回調(diào)函數(shù)】,然后有條件判斷的嵌套回調(diào)(回調(diào)地獄)問題來了;
老司機推薦用【事件】,然后異步流程里有順序依賴;
老司機推薦用【Promise】,然后有順序依賴的流程里,居然還想訂閱事件;
老司機建議試試【協(xié)程】,誰知對方想要合并兩個異步調(diào)用;
……
以上,是異步編程里要面對的一些難題,也是【ReactiveX API】 所致力解決的
一、Promise的用法
- Promise 的含義
- 基本用法
- Promise.prototype.then()
- Promise.prototype.catch()
- Promise.prototype.finally()
- Promise.all()
- Promise.race()
- Promise.resolve()
- Promise.reject()
- 應用
- Promise.try()
Promise 的含義
Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強大。它由社區(qū)最早提出和實現(xiàn),ES6 將其寫進了語言標準,統(tǒng)一了用法,原生提供了Promise對象。
所謂Promise:
簡單說就是一個容器,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果。
從語法上說,Promise 是一個對象,從它可以獲取異步操作的消息。Promise 提供統(tǒng)一的 API,各種異步操作都可以用同樣的方法進行處理。
Promise對象有以下兩個特點。
(1)對象的狀態(tài)不受外界影響。Promise對象代表一個異步操作,有三種狀態(tài):pending(進行中)、fulfilled(已成功)和rejected(已失敗)。只有異步操作的結(jié)果,可以決定當前是哪一種狀態(tài),任何其他操作都無法改變這個狀態(tài)。這也是Promise這個名字的由來,它的英語意思就是“承諾”,表示其他手段無法改變。
(2)一旦狀態(tài)改變,就不會再變,任何時候都可以得到這個結(jié)果。
Promise對象的狀態(tài)改變,只有兩種可能:
從pending變?yōu)?code>fulfilled
從pending變?yōu)?code>rejected。
只要這兩種情況發(fā)生,狀態(tài)就凝固了,不會再變了,會一直保持這個結(jié)果,這時就稱為 resolved(已定型)。
如果改變已經(jīng)發(fā)生了,你再對Promise對象添加回調(diào)函數(shù),也會立即得到這個結(jié)果。這與事件(Event)完全不同,事件的特點是,如果你錯過了它,再去監(jiān)聽,是得不到結(jié)果的。
注意,為了行文方便,本章后面的resolved統(tǒng)一只指fulfilled狀態(tài),不包含rejected狀態(tài)。
有了Promise對象,就可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調(diào)函數(shù)。此外,Promise對象提供統(tǒng)一的接口,使得控制異步操作更加容易。
Promise也有一些缺點。
1、無法取消Promise,一旦新建它就會立即執(zhí)行,無法中途取消。
2、如果不設置回調(diào)函數(shù),Promise內(nèi)部拋出的錯誤,不會反應到外部。
3、當處于pending狀態(tài)時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)。
如果某些事件不斷地反復發(fā)生,一般來說,使用 Stream 模式是比部署Promise更好的選擇。
如果我們有幾個異步操作,并且后一個操作需要前一個操作返回的數(shù)據(jù)才能執(zhí)行,這樣按照Node的一般執(zhí)行規(guī)律,要實現(xiàn)有序的異步操作,通常是一層加一層嵌套下去。
為了解決這個問題,ES6提出了Promise的實現(xiàn)。
含義
Promise 對象用于一個異步操作的最終完成(或失?。┘捌浣Y(jié)果值的表示。
簡單點說,它就是用于處理異步操作的,異步處理成功了就執(zhí)行成功的操作,異步處理失敗了就捕獲錯誤或者停止后續(xù)操作。
例子
var promise1 = new Promise(function(resolve, reject) {
// 2秒后置為接收狀態(tài)
setTimeout(function() {
resolve('success');
}, 2000);
});
promise1.then(function(data) {
console.log(data); // success
}, function(err) {
console.log(err); // 不執(zhí)行
}).then(function(data) {
// 上一步的then()方法沒有返回值
console.log('鏈式調(diào)用:' + data); // 鏈式調(diào)用:undefined
}).then(function(data) {
// ....
});
在這里我們主要關注promise1.then()方法調(diào)用后返回的Promise對象的狀態(tài),是pending還是fulfilled,或者是rejected?
返回的這個Promise對象的狀態(tài)主要是根據(jù)promise1.then()方法返回的值,大致分為以下幾種情況:
如果then()方法中返回了一個參數(shù)值,那么返回的Promise將會變成接收狀態(tài)。
如果then()方法中拋出了一個異常,那么返回的Promise將會變成拒絕狀態(tài)。
如果then()方法調(diào)用resolve()方法,那么返回的Promise將會變成接收狀態(tài)。
如果then()方法調(diào)用reject()方法,那么返回的Promise將會變成拒絕狀態(tài)。
如果then()方法返回了一個未知狀態(tài)(pending)的Promise新實例,那么返回的新Promise就是未知狀態(tài)。
如果then()方法沒有明確指定的resolve(data)/reject(data)/return data時,那么返回的新Promise就是接收狀態(tài),可以一層一層地往下傳遞。
轉(zhuǎn)換實例如下:
var promise2 = new Promise(function(resolve, reject) {
// 2秒后置為接收狀態(tài)
setTimeout(function() {
resolve('success');
}, 2000);
});
promise2
.then(function(data) {
// 上一個then()調(diào)用了resolve,置為fulfilled態(tài)
console.log('第一個then');
console.log(data);
return '2';
})
.then(function(data) {
// 此時這里的狀態(tài)也是fulfilled, 因為上一步返回了2
console.log('第二個then');
console.log(data); // 2
return new Promise(function(resolve, reject) {
reject('把狀態(tài)置為rejected error'); // 返回一個rejected的Promise實例
});
}, function(err) {
// error
})
.then(function(data) {
/* 這里不運行 */
console.log('第三個then');
console.log(data);
// ....
}, function(err) {
// error回調(diào)
// 此時這里的狀態(tài)也是fulfilled, 因為上一步使用了reject()來返回值
console.log('出錯:' + err); // 出錯:把狀態(tài)置為rejected error
})
.then(function(data) {
// 沒有明確指定返回值,默認返回fulfilled
console.log('這里是fulfilled態(tài)');
});
下面代碼中,booksPromise和userPromise是兩個異步操作,只有等到它們的結(jié)果都返回了,才會觸發(fā)pickTopRecommentations這個回調(diào)函數(shù)。
const databasePromise = connectDatabase();
const booksPromise = databasePromise
.then(findAllBooks);
const userPromise = databasePromise
.then(getCurrentUser);
Promise.all([
booksPromise,
userPromise
])
.then(([books, user]) => pickTopRecommentations(books, user));
下面代碼中,如果 5 秒之內(nèi)fetch方法無法返回結(jié)果,變量p的狀態(tài)就會變?yōu)閞ejected,從而觸發(fā)catch方法指定的回調(diào)函數(shù)。
const p = Promise.race([
fetch('/resource-that-may-take-a-while'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
]);
p
.then(console.log)
.catch(console.error);