PWA(Progressive Web App)入門系列:(四)Promise
前言
這一章說一下ES6的Promise物件。為什麼要在PWA系列的文章中講Promise呢?因為PWA中的許多技術API中都是以Promise返回的方式返回的,為了對後續章節中PWA技術API更好的理解,這裡就來說一個Promise物件。
Promise出現的背景
在JavaScript當中,處理非同步操作時,我們需要知道操作是否已經完成,當執行完成的時候會返回一個回撥函式,表示操作已經完成。所以在處理非同步操作時,通常是使用回撥巢狀的方式(CallBack)。但是如果出現多層回撥巢狀,也就是我們常說的回撥金字塔(Pyramid of Doom),絕對是一種糟糕的程式設計體驗。
像這樣:
function a1() {
function a2() {
function a3() {
function a4() {
function a5() {
...
}
}
}
}
}
回撥方式主要會導致兩個關鍵問題:
- 巢狀太深程式碼可讀性太差
- 行邏輯必須序列執行。
在這種情況下,Promise 物件出現了。2015 年 6 月,加入了ECMAScript 6 的標準。
Promise簡介
Promise 物件用於一個非同步操作的最終完成(或失敗)及其結果值的表示。一個 Promise 物件代表一個目前還不可用,但是在未來的某個時間點可以被解析的值。它允許你以一種同步的方式編寫非同步程式碼。Promises 將巢狀的回撥改造成一系列的.then
上面的程式碼,用Promise的方式可以寫成:
a1.then(function(data) {
return a2(data)
})
.then(function(data) {
return a3(data)
})
.then(function(data) {
return a4(data)
})
.then(function(data) {
return a5(data)
})
...
Promise 物件有以下兩個特點:
- 物件的狀態不受外界影響。 Promise 物件代表一個非同步操作,有三種狀態:Pending(進行中)、Resolved(已完成,又稱 Fulfilled)、 Rejected(已失敗)。根據非同步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。這也是 Promise 這個名字的由來,它的英語意思就是“承諾”,表示無法通過其他手段改變。
- 一旦狀態改變,就不會再變,任何時候都可以得到這個結果。 Promise 物件的狀態改變,只有兩種可能:從 Pending 變為 Resolved 和從 Pending 變為 Rejected。只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果。就算改變已經發生了,你再對 Promise 物件添加回調函式,也會立即得到這個結果。
Promise狀態:
語法
new Promise( function(resolve, reject) {...} /* executor */ );
executor
是一個帶有 resolve
和 reject
兩個引數的函式 。executor
函式在Promise建構函式執行時同步執行,被傳遞 resolve
和 reject
函式(executor
函式在Promise建構函式返回新建物件前被呼叫)。resolve
和 reject
函式被呼叫時,分別將promise的狀態改為fulfilled(完成)或rejected(失敗)。executor
內部通常會執行一些非同步操作,一旦完成,可以呼叫resolve
函式來將promise狀態改成fulfilled,或者在發生錯誤時將它的狀態改為rejected。
如果在executor
函式中丟擲一個錯誤,那麼該promise 狀態為rejected。executor函式的返回值被忽略。
基本用法
new Promise(function(resolve, reject) {
// ... some code
if (/* 操作成功 */){
resolve(value);
} else {
reject(error);
}
});
注意:例項化的Promise物件會立即執行
方法
下面說一下Promise物件的方法。
Promise.prototype.then()
then方法是定義在原型物件Promise.prototype上的。它最多需要有兩個引數:Promise 的成功和失敗情況的回撥函式。then
方法的第一個引數是resolved狀態的回撥函式,第二個引數(可選)是rejected狀態的回撥函式。
then
方法返回的是一個新的Promise例項。因此可以採用鏈式寫法,即then
方法後面再呼叫另一個then
方法。
Promise.prototype.catch()
catch()
方法返回一個Promise,只處理拒絕的情況。它的行為與呼叫Promise.prototype.then(undefined, onRejected)
相同。用於指定發生錯誤時的回撥函式。
taskkA()
.then(function() {
return taskB()
})
.then(function() {
return taskC()
})
.catch(function(err) {
// ...
})
.then(function() {
return taskD()
})
Promise.resolve()
返回一個狀態由給定value決定的Promise物件。如果該值是一個Promise物件,則直接返回該物件;如果該值是thenable(即,帶有then方法的物件),返回的Promise物件的最終狀態由then方法執行決定;否則的話(該value為空,基本型別或者不帶then方法的物件),返回的Promise物件狀態為fulfilled,並且將該value傳遞給對應的then方法。通常而言,如果你不知道一個值是否是Promise物件,使用Promise.resolve(value) 來返回一個Promise物件,這樣就能將該value以Promise物件形式使用。
Promise.resolve(value)
Promise.resolve(promise)
Promise.resolve(thenable)
有時需要將現有物件轉為 Promise 物件,Promise.resolve方法就起到這個作用。
Promise.resolve('data')
// 等價於
new Promise(resolve => resolve('data'))
Promise.reject()
返回一個狀態為失敗的Promise物件,並將給定的失敗資訊傳遞給對應的處理方法。該例項的狀態為rejected。
var p = Promise.reject('出錯了');
// 等同於
var p = new Promise((resolve, reject) => reject('出錯了'))
p.catch(function(err) {
console.log(err)
})
// 出錯了
Promise.all()
這個方法返回一個新的promise物件,該promise物件在iterable引數物件裡所有的promise物件都成功的時候才會觸發成功,一旦有任何一個iterable裡面的promise物件失敗則立即觸發該promise物件的失敗。這個新的promise物件在觸發成功狀態以後,會把一個包含iterable裡所有promise返回值的陣列作為成功回撥的返回值,順序跟iterable的順序保持一致;如果這個新的promise物件觸發了失敗狀態,它會把iterable裡第一個觸發失敗的promise物件的錯誤資訊作為它的失敗錯誤資訊。Promise.all方法常被用於處理多個promise物件的狀態集合。
Promise.all(iterable)
iterable: 一個可迭代物件,例如一個 Array 或 String。
上述可迭代物件中的所有 Promise 被 resolve 之後返回 resolve,或者在任一 Promise 被 reject 後返回 reject。
Promise.race()
當iterable引數裡的任意一個子promise被成功或失敗後,父promise馬上也會用子promise的成功返回值或失敗詳情作為引數呼叫父promise繫結的相應控制代碼,並返回該promise物件。
用法和Promise.all()類似。
Promise.race(iterable);
基本應用
下面根據Promise的特性,做幾個例子。
非同步載入圖片
function loadImage(url) {
return new Promise(function(resolve, reject) {
var img= new Image();
img.onload = function() {
resolve(img);
};
img.onerror = function(err) {
reject(new Error(err));
};
img.src = url;
});
}
網路請求超時處理
Promise.race([
fetch('http://xxxx.xxx'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('請求超時')), 6000)
})
]).
then(function(data) {
// ...
})
.catch(function(err) {
// 處理錯誤...
})
總結
這一篇裡,對Promise的背景由來,及相關方法進行了相應的介紹說明,也瞭解到了Promise在非同步處理上的使用優勢。
部落格名稱:王樂平部落格
CSDN部落格地址:http://blog.csdn.net/lecepin