目錄
初步瞭解Promise
所謂Promise,字面上可以理解為“承諾”,表示一次操作的結果。和一般操作不同的是,Promise 物件不是實時的,而是在未來的某一時刻,會有返回,是一個能代表未來出現的結果的promise物件。 Promise允許你為非同步操作的成功和失敗分別繫結相應的處理方法(handlers)。
我們知道,JavaScript 在瀏覽器中是單執行緒呼叫的,但是有時我們又需要非同步操作,例如訪問檔案,或者是呼叫 ajax。這時候,我們就需要 Promise 了。
一個Promise有三種狀態:
- pending:初始(等待)態,既不是成功,也不是失敗
- fulfilled(resolved):意味著操作成功完成
- rejected:意味著操作失敗
一個Promise的狀態只可能從“等待”轉到“完成”態或者“拒絕”態,不能逆向轉換,同時“完成”態和“拒絕”態不能相互轉換
Promise實現的類庫
為什麼需要這些類庫?
為什麼需要這些類庫呢?我想有些讀者不免會有此疑問。首先能想到的原因是有些執行環境並不支援 ES6 Promises 。
Promise相容如下
Promise擴充套件類庫
Promise擴充套件類庫除了實現了Promise中定義的規範之外,還增加了自己獨自定義的功能。
Promise擴充套件類庫數量非常的多,到底選擇哪個來使用完全看自己的喜好了,下面我們只提到其中兩個比較有名的
petkaantonov/bluebird
這個類庫除了相容 Promise 規範之外,還擴充套件了取消promise物件的執行,取得promise的執行進度,以及錯誤處理的擴充套件檢測等非常豐富的功能,此外它在實現上還在效能問題下了很大的功夫。
Bluebird 提供了很多方法來幫助我們把現有的庫函式由 Callback 的方式,轉化為 Promise 的方式,這叫做 Promise 化。最常見的,例如 Node 中預設提供的 fs 檔案操作控制程式碼,我們可以通過 Promise.promiseifyAll(fs) 的方式,把 fs 的呼叫,通過 Promise 返回。
let Promise = require("bluebird");
let fs = require("fs");
Promise.promisifyAll(fs);//這裡 fs 被改寫了,增加了 Async 字尾的方法
fs.readFileAsync('./welcome.txt', 'utf8').then((data) =>{
console.log(data);
}
複製程式碼
kriskowal/q類庫
Q 實現了 Promises 和 Deferreds 等規範。 它自2009年開始開發,還提供了面向Node.js的檔案IO API Q-IO 等, 是一個在很多場景下都能用得到的類庫。
深度解析Promise + 原始碼實現
new Promise( function(resolve, reject) {...} );
複製程式碼
引數
在new Promise 時,需要傳遞一個executor 執行器,執行器會立刻執行。執行器中傳遞兩個引數, resolve reject。
- resolve:非同步操作執行成功後的回撥函式
- reject:非同步操作執行失敗後的回撥函式
原始碼:new Promise原始碼實現,github檢視程式碼 點選檢視原始碼
Promise 原型方法
then
Promise.prototype.then(onFulfilled, onRejected)
複製程式碼
onFulfilled
當Promise成功態(fulfillment)時,該引數作為回撥函式被呼叫。該函式有一個引數,即成功時返回的最終結果(value)
onRejected
當Promise失敗態(rejection )時,該引數作為回撥函式被呼叫。該函式有一個引數,即拒絕的原因(reason)。
詳細解讀
- 每個Promise都有then方法(可以說,then就是promise的核心),而且then必須返回一個promise
- 同一個Promise的then可以呼叫多次,並且回撥的執行順序跟它們被定義時的順序一致
- then方法接受兩個引數,第一個引數是成功時的回撥,在promise由“等待”態轉換到“完成”態時呼叫,另一個是失敗時的回撥,在promise由“等待”態轉換到“拒絕”態時呼叫
- 如果then中返回的是一個結果的話,會把這個結果傳遞給下一個then
- 如果then返回的是一個promise的話,會等待這個promise執行完,決定返回的那個 promise是成功還是失敗,如果成功走下一個then的成功
- 每個then都返回一個新的promise,為什麼返回一個新的promise而不是this,因為 promise狀態確定好,就不能更改
then是否返回一個新的Promse,測試程式碼如下:
let promise1 = new Promise(function(resolve){
resolve(1);
});
let thenPromise = promise1.then(function(value){
console.log(value);
});
let catchPromise = thenPromise.catch(function(error){
console.log(error);
});
console.log(promise1 !== thenPromise); // true
console.log(thenPromise !== catchPromise); //true
複製程式碼
如上程式碼,列印的都是true,這說明不管是then還是catch都返回的和新建立的promise是不同的物件
原始碼:上面關於then的詳細解讀已經描述的很詳細了,實現程式碼見github 點選檢視原始碼
catch
Promise.prototype.catch(onRejected)
複製程式碼
onRejected
當Promise 被rejected時,被呼叫的一個Function。 該函式擁有一個引數:reason 即rejection 失敗時返回的原因。
詳細解讀
- 和then一樣返回結果為新的promise
- 捕獲then沒有捕獲到的異常,一般放在最後,放在中間沒有意義
let p1 = Promise.resolve('成功')
p1.then(() => {
console.log('p1 then')
throw new Error();
}).catch(err => {
console.log('catch ' + err);
})
//結果:
// p1 then
// catch Error
複製程式碼
注:即使你堅信不會出現異常,新增一個 catch() 總歸是更加謹慎的。如果你的假設最終被發現是錯誤的,它會讓你的生活更加美好。
方法
1. resolve
返回一個狀態為成功的Promise物件,並將給定資料結果傳遞給對應的處理方法
Promise.resolve(value)
複製程式碼
2. reject
返回一個狀態為失敗的Promise物件,並將給定的失敗資訊傳遞給對應的處理方法
Promise.resolve(reason)
複製程式碼
3. all
引數為陣列,返回陣列中所有的請求結果,並且返回資料的順序與請求的陣列順序一致。返回時間以最慢的為準(要麼全部成功,要麼全部失敗)
// 要麼全部成功
let p = [
new Promise((resolve, reject) => resolve(1)),
new Promise((resolve, reject) => resolve(2))
]
Promise.all(p).then(res => {
console.log(res) // [1, 2]
})
// 要麼就失敗
let p1 = [
new Promise((resolve, reject) => resolve(1)),
new Promise((resolve, reject) => reject(2))
]
Promise.all(p1).then(res => {
console.log(res)
},err => {
console.log(err); // 2
})
複製程式碼
原始碼:Promise.all()原始碼實現,可直接下載測試上面例子 點選檢視原始碼
4. race
引數為陣列,返回的結果以最快的為準,且只有一個結果(誰最快就返回誰的結果)
// 要麼全部成功,返回執行完最快的結果
let p = [
new Promise((resolve, reject) => resolve(1)),
new Promise((resolve, reject) => resolve(2))
]
Promise.race(p).then(res => {
console.log('success ' + res) // success 1或success 2 哪個執行完成的快返回哪個結果
})
// 要麼就失敗
let p1 = [
new Promise((resolve, reject) => reject(1)),
new Promise((resolve, reject) => resolve(2))
]
Promise.race(p1).then(res => {
console.log('success' + res)
},err => {
console.log('err'); // err
})
複製程式碼
原始碼:Promise.race()原始碼實現,可直接下載測試上面例子 原始碼實現見gitHub
以上詳細解讀,能幫助您更加了解Promise,下面一個經典的圖,幫助大家更好的理解promise
完整原始碼
為了方便大家測完整程式碼,詳見gitHub
總結
非同步程式設計,它幫助我們避免同步程式碼帶來的執行緒阻塞的問題,過多的回撥巢狀很容易會讓我們陷入“回撥地獄”中,使程式碼變成一團亂麻。使用Promise,可以有效的解決這個問題,並且還可以使我們的程式碼更加清晰、易讀。
參考閱讀:
作者:香香
將來的你,一定會感謝現在拼命努力的自己!