JS 中的 Promise

wopen發表於2019-03-02

Promise 是非同步程式設計的解決方案,它可以很方便的處理非同步事件。一個 Promise 例項包含一個非同步操作,這項非同步操作只有三種狀態,pending 初始狀態,既不是成功,也不是失敗狀態,Resolved 成功完成,Rejected 非同步操作失敗。

Promise 的狀態不受外界影響而且一旦狀態改變,就不會再改變。

建立 Promise

要建立 Promise 例項要用到 Promise 類,它接受一個函式作為引數,函式接受兩個引數,這兩個引數也是函式,一個是當非同步成功執行時需要呼叫的函式,一個是失敗時呼叫的函式。

建立 Promise 例項的引數函式,將會立即執行。

function getData(url) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open("GET", url);
        xhr.onload = () => resolve(xhr.responseText); 
        // 將狀態變為 Resolved 並傳入獲取到的資料
        xhr.onerror = () => reject(xhr.statusText);
        // 將狀態變為 Rejected 並傳入錯誤資訊
        xhr.send();
    })
}

//呼叫 getData 將返回一個 Promise 例項
複製程式碼

Promise 的原型方法

Promise 一共有三個原型方法,then, catchfinally

then

then 方法接受兩個函式引數,一個是成功時候執行,一個是失敗時執行。

而且then方法將會返回一個新的 Promise 例項,這就代表可以進行鏈式操作。

立即呼叫resolvePromise,它then方法的引數函式會放到本次事件迴圈末尾。

let p1 = getData('http://www.a.cn')

let onFulfilled = data => console.log(data)
let onRejected = err => console.log(err)

p1.then(onFulfilled, onRejected)

// 當成功時將會呼叫第一個回撥函式,並把 resolve 傳入的引數再全部傳入 onFulfilled 中

// 當成功時將會呼叫第二個回撥函式,並把 resolve 傳入的引數再全部傳入 onRejected 中

let p2 = getData('http://www.a.cn')

p2.then(url => getData(url))
  .then(data => console.log(data)) // 鏈式操作
  
// ------------------

let p = new Promise(resolve => {
    console.log(1)
    resolve(3)
})

console.log(2)

setTimeout(() => console.log(4))
p.then(num => console.log(num))
// 列印順序是 1 2 3 4

複製程式碼

catch

catch 方法接受一個函式引數onRejectedcatch其實就是then的第二個引數。

catch方法一般放在最後面,前面then方法,將會冒泡到它這裡。

catch 返回的還是一個 Promise 物件。不過catch捕獲不了後面的then方法丟擲的錯誤當然catch方法中也可以丟擲錯誤。

let p3 = getData('http://a.com')

p3.then(data => console.log(data))
  .then(() => throw new Error('error')) // 丟擲錯誤
  .then(() => console.log(1))
  .catch(err => console.log(err))
 
// catch 將會捕獲上面的錯誤,而且第三個 then 方法會被跳過
複製程式碼

Promise 中未捕獲的錯誤不會終止指令碼執行而是列印一個未捕獲 promise 錯誤提示。

finally

finally 方法接受一個函式引數onFinally。它不管 Promise 物件最後狀態如何,都會執行的操作。finally方法的回撥函式不接受任何引數。

它返回一個設定了 finally 回撥函式的Promise物件。

Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  );
};

// finally 相當於返回一個已經帶兩個引數的 then 方法
複製程式碼

Promise 方法

Promise 一共有 4 個靜態方法。分別是all, race, resolvereject

resolve

resolve 接受一個引數,返回一個狀態由給定value決定的Promise物件。

如果引數是 Promise 物件則直接返回。

如果引數是thenable(即,帶有then方法的物件),then 方法會作為Promise的引數,立即執行。

如果是其他值返回一個新的 Promise 物件,狀態為resolved,該value傳遞給對應的then方法。

Promise.resolve(1).then(num => {
    console.log(num)
});
複製程式碼

reject

reject 方法接受一個引數,返回一個狀態為失敗的Promise物件,並將給定的失敗資訊傳遞給對應的處理方法。

Promise.reject('error');
// 等同於
new Promise((resolve, reject) => reject('error'))
複製程式碼

all

all 方法接受具有 iterable 介面 引數。

這個方法返回一個新的promise物件,該promise物件在iterable引數物件裡所有的promise物件都成功的時候才會觸發成功,一旦有任何一個iterable裡面的promise物件失敗則立即觸發該promise物件的失敗。

這個新的promise物件在觸發成功狀態以後,會把一個包含iterable裡所有promise返回值的陣列作為成功回撥的返回值,順序跟iterable的順序保持一致

如果這個新的promise物件觸發了失敗狀態,它會把iterable裡第一個觸發失敗的promise物件的錯誤資訊作為它的失敗錯誤資訊。

引數中的每個專案都是一個 Promise 物件,如果不是則用 resolve 方法轉換成 Promise 物件。

race

race 方法和 all 方法一樣,但是當iterable引數裡的任意一個子 Promise 被成功或失敗後,返回的 Promise 物件的狀態就變成這個子 Promise 物件的狀態,它的值或錯誤資訊也會傳遞給返回 Promise 物件的回撥函式。

Promise 的屬性

Promise 一共有兩個屬性,lengthprototype

length屬性,其值總是為 1 (構造器引數的數目)。

相關文章