Promise ES6

AlenQi發表於2018-02-12

Foreword

promise

什麼是Promise?

Promise是抽象非同步處理物件以及對其進行各種操作的元件。Promise最早被提出與基於並列/並行處理設計的***E語言***,JavaScript也因為Promise擁有5投入這種特性。

ES6 Promises的規範來源於Promises/A+社群,它有很多版本的實現。

  • 用new Promise 例項化的promise物件有三個狀態。
    1. Pending
    2. Fulfilled
    3. Rejected

根據ES6 Promises定義的API大致可以分為下面三種

  • Constructor

    • Promise類似於 XMLHttpRequest,從建構函式 Promise 來建立一個新建新promise物件作為介面。 要想建立一個promise物件、可以使用new來呼叫Promise的構造器來進行例項化

      let promise = new Promise(function(resolve, reject) {
          // 非同步處理
          // 處理結束後、呼叫resolve 或 reject
      })
      複製程式碼

      promise物件剛被建立後的初始化狀態就是 Pending

  • Instance Method

    • 對通過new生成的promise物件為了設定其值在 resolve(成功) / reject(失敗)時呼叫的回撥函式 可以使用promise.then() 例項方法。

      promise.then(onFulfilled, onRejected)
      複製程式碼
    • resolve(成功)時 此時的物件狀態改變為 Fulfilled onFulfilled 會被呼叫

    • reject(失敗)時 此時的物件狀態改變為 Rejected onRejected 會被呼叫

    promise物件的狀態,從Pending轉換為Fulfilled或Rejected之後, 這個promise物件的狀態就不會再發生任何變化。也就是說,在[.then]後執行的函式只會被呼叫一次。

  • Static Method

    • Promise 全域性物件還擁有一些靜態方法

Promise如何用?

  • 我們先用Promise來通過非同步處理方式來獲取XMLHttpRequest(XHR)的資料看看
function getURL(URL) {
    return new Promise(function (resolve, reject) {
        let req = new XMLHttpRequest()
        req.open('GET', URL, true)
        req.onload = function () {
            if (req.status === 200) {
                resolve(req.status)
            } else {
                reject(new Error(req.statusText))
            }
        }
        req.onerror = function () {
            reject(new Error(req.statusText))
        }
        req.send()
    })
}
// 成功時呼叫resolve
// 失敗時呼叫reject
let URL = "http://AlenQi.site/get"
// 拋錯方法一
getURL(URL).then(function onFulfilled(value){
    console.log(value)
},function onRejected(error){
    console.error(error)
})
// 拋錯方法二
getURL(URL).then(function onFulfilled(value){
    console.log(value)
}).catch(function onRejected(error){
    console.error(error)
})
//  被resolve處理後會將promise物件變為Fulfilled狀態,
//  並呼叫onFulfilled函式,即第一個引數

//  被reject處理後會將promise物件變為Rejected狀態,
//  並呼叫onRejected函式,即第二個引數或者catch中的函式
複製程式碼

Promise的同步與非同步

  • 在promise狀態就算是在註冊時立即轉變為Settled(不變的),Promise也會以非同步的方式呼叫該回撥函式,這是在Promise設計時的規定
let promise = new Promise(function (resolve){
    console.log("1") // 1
    resolve(2)
})
promise.then(function(value){
    console.log(value) // 3
})
console.log("3") // 2

// 輸出結果為1、3、2
複製程式碼
  • 這樣的設計是合理且符合規範的,如果是同步呼叫那麼promise狀態轉變是否是立即轉變就會影響程式執行的順序,難以控制預期。
  • Effective JavaScript也對同步非同步有所解釋

絕對不能對非同步回撥函式(即使在資料已經就緒)進行同步呼叫。 如果對非同步回撥函式進行同步呼叫的話,處理順序可能會與預期不符,可能帶來意料之外的後果。 對非同步回撥函式進行同步呼叫,還可能導致棧溢位或異常處理錯亂等問題。 如果想在將來某時刻呼叫非同步回撥函式的話,可以使用 setTimeout 等非同步API。

Promise的方法鏈

  • Promise.prototype.then方法返回的是一個新的Promise物件,因此可以採用鏈式寫法。由於這種promise chain,promise適合處理這種非同步處理較多的應用場景。
let correctChain = new Promise(function (resolve) {
    resolve(1)
})
correctChain.then(function(value) {
  // first
  // value === 1
  return value + 1
}).then(function(value) {
  // second
  // value === 2
  return value + 1
}).then(function(value) {
  // third
  // value === 3  
  return value + 1
}).catch(function onRejected(error){
    console.log(error)
})
複製程式碼

promise chain - 方法鏈越短越好。

  • 這裡then沒有第二個方法,在發生異常的時候,或者狀態變為reject是會被catch捕獲。我們只能捕獲到錯誤,無法判斷錯誤來源,我們也無法判斷錯誤是主動丟擲還是其他異常導致的,這也是建議reject而不是throw。
  • 方法鏈值的傳遞,傳給每個then方法的value的值都是前一個promise物件通過return返回的值。
  • 每次 promise.then 呼叫都會返回一個新建立的promise物件,所以鏈式呼叫不能寫在成下面的樣子
let errorChain = new Promise(function (resolve) {
    resolve(1)
})
errorChain.then(function (value) {
    return value * 2
})
errorChain.then(function (value) {
    return value * 2
})
errorChain.then(function (value) {
    console.log("1: " + value)  // => 100
})
複製程式碼
  • 這樣傳給每個then的value值都是相同的1,而且幾乎是同時呼叫的

Promise.all&&Promise.race

  • Promise.all
Promise.all(promiseArray)
// 生成並返回一個新的promise物件
let array1 = Promise.resolve(1)
let array2 = Promise.resolve(2)
let array3 = Promise.resolve(3)
Promise.all([array1, array1, array1]).then(function (results) {
  console.log(results) // results === [1,2,3]
  //引數傳遞promise陣列中所有的promise物件都變為resolve的時候,該方法才會返回,
  //新建立的promise則會使用這些promise的值
  //如果引數中的任何一個promise為reject的話,則整個Promise.all呼叫會立即終止,
  //並返回一個reject的新的promise物件。
})
複製程式碼
  • Promise.race
Promise.race(promiseArray);
// 生成並返回一個新的promise物件
let array1 = Promise.resolve(1)
let array2 = Promise.resolve(2)
let array3 = Promise.resolve(3)
Promise.race([array1, array1, array1]).then(function (results) {
  console.log(results) // results === 1
  //promise 陣列中的任何一個promise物件如果變為resolve或者reject的話, 該函式就會返回,
  //並使用這個promise物件的值進行resolve或者reject。
})
複製程式碼

相關文章