ES6入門之Promise對象

  • 2019 年 10 月 9 日
  • 筆記

1. Promise 的含義

Promise 是異步編程的一種解決方案,比傳統的解決方案–回調函數和事件更合理、更強大。

1.1 什麼是Promise

簡單來說就是一個容器,裏面保存着某個未來才會結束的事件(也就是異步操作)的結果。從語法上來講,Promise是一個對象,從它可以獲取異步操作的消息,它提供統一的API,各種異步操作都可以用同樣的方法進行處理。

Promise有兩個特點:

1.1.1、對象的狀態不受外界影響。Promise對象代表一個異步操作,有三種狀態:pending(進行中)、fulfilled(已成功)和 rejected(已失敗)。只有異步操作的結果,可以決定當前是哪一種狀態、任何其他操作都無法改版這個狀態。

1.1.2、一旦狀態改版,就不會再變,任何時候都可以得到這個結果。Promise對象的狀態改變,只存在兩種可能:從 pending 變為 fulfilled 和 從 pending 變為 rejeced。只要這兩種情況發生,狀態就終止,不會再變了並一直保持這個結果。這時就稱為 resolved(已定型)。如果改版已經發生了,即使再對Promise對象添加回調函數,也會立即得到這個結果。如果你錯過了再想去監聽,是得不到結果的。

1.1.3、有了Promise對象,就可以將異步操作以同步操作的流程顯示出來,這樣就避免了層層嵌套的回調函數。Promise對象提供統一的接口,使得控制異步操作更加容易。

1.1.4、Promise也有一些缺點,就是無法取消 Promise,一旦建立就會立即執行,無法中途取消。如果不設置回調函數,Promise內部拋出的錯誤不會反應到外部。另外如果處於 pending 狀態時,是無法知道現在到了哪一個階段。

2. 基本用法

Promise對象是一個構造函數,用來生成Promise實例

const promise = new Promise((reolve, reject) => {      if (// 異步操作成功) {          resolve(val)      }else{          reject(val)      }  })

Promise 構造函數接受一個函數作為參數,該函數的兩個參數分別是 resolve 和 reject。

resolve:,將Promise對象的狀態從『未完成』變為『成功』(pending => resolved),在異步操作成功時調用,並將異步操作的結果作為參數傳遞出去。

reject:將Promise對象的狀態從『未完成』變為『失敗』(pending => rejected),在異步操作失敗時調用,並將異步操作的結果作為參數傳遞出去。

Promise 實例生成以後,可以用 then 方法分別指定 resolved 狀態和 rejected 狀態的回調函數。

promise.then((val) => {      // success  },(err) => {      // failure  })

then方法可以接受兩個回調函數作為參數。(第二個函數可選,這兩個函數都接受Promise對象傳出的值作為參數)

1、第一個回調函數在Promise對象的狀態變為『resolved』時調用。

2、第二個回調函數在Promise對象的狀態變為『rejected』時調用。

實例:    function timeout(ms) {      retrun new Promise((resolve, reject) => {          setTimeout(resolve, ms, 'done')      })  }    timeout(100)  .then((v) => {      console.log(v)  })

上面代碼中,timeout方法返回一個Promise實例,表示一段時間以後才會發生的結果,過了 ms時間後,Promise狀態變為『resolved』然後就會觸發then方法綁定的回調函數。

Promise 建立後就會立即執行

let promise = new Promise((resolve, reject) => {      console.log('Promise')      resolve()  })    promise  .then(() => {      console.log('resolved')  })    console.log('hh')    // Promise  // hh  // resolved

Promise建立後立即執行,首先輸出 「Promise」然後執行promise 的then函數,然後首先執行同步任務 輸出 hh 在執行 then方法的回調函數輸出resolved

如果調用 resolve 函數和 reject 函數時帶有參數,那麼它們的參數會被傳遞給回調函數。reject函數的參數通常是Error對象的實例,表示拋出的錯誤。resolve函數的參數除了正常的值以外,還有可能是一個Promise實例。resolve實在成功的時候調用,reject是在失敗的時候調用。

const p1 = new Promise(function (resolve, reject) {    // ...  });    const p2 = new Promise(function (resolve, reject) {    // ...    resolve(p1);  })

上述代碼中:p1 和 p2都是Promise的實例,但是p2的 resolve方法將 p1作為參數,即一個異步操作的結果返回是另一個異步操作。

注意:p1的狀態就會傳遞給p2,p1的狀態決定了p2的狀態。如果p1的狀態是pending,那麼p2的回調函數就會等待p1的狀態改變;如果p1的狀態已經是 resolved 或者 rejected,那麼p2的回調函數會立即執行。

一般來說,調用resolve 或 reject以後,Promise的進程就就結束了,後續操作應該放到 then方法里,而不是直接寫在 resolve 或 reject 的後面。另外最後在它們之前加上 return語句。

3. Promise.prototype.then()

Promise實例具有 then 方法,then方法是定義在原型對象 Promise.prototype上的。它的作用是為 Promise 實例添加狀態改變時的回調函數。then 的第一個參數是 resolved狀態的回調函數,第二個參數是 rejected狀態的回調函數。

then方法返回的是一個新的 Promise 實例,不是原來那個,因此可以使用鏈式寫法。.then().then()

a().then((j) => {      retrun j  }).then((i) => {      console.log(i)  },(err)=>{      console.log(err)  })

上面 第一個then方法指定的回調函數,返回的是另一個 Promise 對象。這時,第二個 then 方法指定的回調函數,就會等這個新的 Promise對象狀態發生變化,如果變為 resolved,就調用第一個回調函數,如果狀態變為 rejected,就調用第二個回調函數。

4. Promise.prototype.catch()

Promise.prototype.catch 方法是 .then(null, rejecton) 或 .then(undefined, rejection)的別名,用於指定發生錯誤時的回調函數。

a().then((p) => {      // success  }).catch((err) => {      console.log('err')  })

如果對象的狀態變為 resolved, 則會調用 then 方法指定的回調函數 success,如果異步操作拋出錯誤,狀態就會變為 rejected,就會調用 catch 方法指定的回調函數處理這個錯誤。如果 then 方法指定的回調函數,在運行中拋出錯誤,也會被catch 方法捕獲。

另外reject方法的作用等同於拋出錯誤

如果 Promise狀態已經變成 resolved,再拋出錯誤是無效的。因為狀態一旦改版,就永遠保持,不會再變了。 而且Promise的錯誤有『冒泡』的性質,會一直向後傳遞,直到被捕獲位置,它的錯誤總會被下一個catch語句捕獲

建議:Promise 對象後面要跟catch方法,這樣可以處理 Promise 內部發生的錯誤。catch方法返回的還是一個 Promise 對象,因此後面還可以接着調用then方法。

注意: catch函數中的方法發生錯誤,如果後面沒有別的catch 方法,那麼錯誤將不會被捕獲,如果 catch 後面 還有catch ,第二個catch將會捕獲前一個catch方法拋出的錯誤。

5. Promise.prototype.finally()

finally 方法用於指定不管 Promise 對象最後狀態如何,都會執行的操作。

promise  .then(result => {···})  .catch(error => {···})  .finally(() => {···});

以上,不管promise 最後的狀態,都會執行 finally 方法指定的函數。

finally 方法的回調函數不接受任何參數,所以就無法知道之前Promise狀態到底是 fulfilled 還是 rejected。所以在finally方法裏面的操作,是與之前狀態無關的而且不依賴於Promise的執行結果。

6. Promise.all()

Promise.all 方法用於將多個 Promise 實例,包裝成一個新的 Promise實例。

const p = Promise.all([p1, p2, p3])

Promise.all 方法接受一個數組作為參數,p1、p2、p3都是Promise實例,如果不是,就會先調用Promise.resolve方法,將參數轉為 Promise 實例再處理。(Promise.all 方法的參數可以不是數組,但必須具有 Iterator 接口,且返回的每個成員都是 Promise 實例。)

Promise.all 的狀態有兩種情況:

1、如果 p1 p2 p3的狀態都變成了 fulfilled,p的狀態才是fulfilled,這時候返回一個 p1 p2 p3返回值組成的數組,傳遞給 p 的回調函數。

2、如果 p1 p2 p3中任一一個被rejected,p 的狀態就變成了 rejected,這時候返回的是第一個被 rejected 實例的返回值,傳遞給 p 的回調函數。

注意,如果作為參數的 Promise 實例,自己定義了catch方法,那麼它一旦被rejected,並不會觸發Promise.all()的catch方法。而是觸發自己定義的catch方法。

7. Promise.race()

Promise.race方法同樣是將多個 Promise 實例,包裝成一個新的 Promise實例。

const p = Promise.race([p1, p2, p3]);

與 Promise.all 的區別就是 p1 p2 p3 中一個實例改變狀態,那麼 p 的狀態就跟着改變了,返回值為最先返回那個Promise實例的返回值。

8. Promise.resolve()

將現有對象轉為 Promise 對象。

1、如果參數是一個Promise 實例
那麼將不做任何修改。
2、參數是一個 thenable對象(具有then方法的對象)
將這個對象轉為Promise對象,然後立即執行 thenable對象的then方法
3、參數不是具有then方法的對象,或根本不是對象
返回一個新的Promise對象,狀態為 resolved
4、不帶任何參數
直接返回一個 resolved 狀態的Promise對象

9. Promise.reject()

Promise.reject(reason)方法也會返回一個新的 Promise 實例,該實例的狀態為rejected。回調函數立即執行。

關注公眾號 【小夭同學】

公眾號小夭同學