帶你重學ES6 | Promsie

Promise 讓人又恨又愛的存在,恨是由於面試的時候會圍繞它出不少題,又繞又頭疼,愛是真香,誰都逃不過真香定律。前端

一、概念

Promise 是異步編程的一個新的解決方案,阮一峯:ECMAScript 6 入門中給出對 promise 的含義是:所謂 Promise,簡單說就是一個容器,裏面保存着某個將來纔會結束的事件(一般是一個異步操做)的結果。 Promise 是一個構造函數,它有兩個特色:git

  1. Promise 有三個狀態:pending(進行中)、resolved(已成功)和 rejected(已失敗)。而且狀態不受外部的影響。
  2. 狀態一旦改變就沒法修改。只能有兩個過程:一個是從 pending 到 resolved 還有一個就是從 pending 到 rejected,不可能從 resolved 到 rejected,一旦成功就不可能再失敗了。
  3. Promise 一旦建立就會當即執行,不能中途取消。

二、用法

一般咱們來講,Promise 主要是解決異步回調地獄。什麼是回調地獄? es6

從網上找了幾張圖,你們能夠感覺一下被回調地獄所支配的恐懼: 回調地獄最大的缺點就是代碼可讀性差,編寫費勁。 接下來咱們來看一下 Promise 的基本用法:github

new Promise((resolve, reject)=>{
 ...  if(success) {  resolve(value);  }else {  reject(error);  } }) 複製代碼

以前說過,Promise 是一個構造函數,它接收一個函數參數,這個函數中接收兩個參數,一個是 resolve 還有一個是 rejected,這兩個參數均爲函數,而且這兩個參數不用本身部署,JS 引擎會自動部署。 resolve 函數的做用是當異步函數成功時,將成功後的值傳遞出去,同時也是將狀態從 pending 變爲 resolved,reject 函數的做用是當異步函數失敗後,將失敗的錯誤信息傳遞出去,同時也是將狀態從 pending 變爲 rejected。面試

2.一、then()

當 Promise 實例建立成功後,能夠執行其原型上的 then 方法,then 方法一樣接收兩個函數參數,第一個是接收的 resolve 回調的結果,第二個是 reject 回調的結果,第二個參數是非必填的。編程

new Promise((resolve, reject) => {
 resolve(1); }).then(  (value) => console.log(value) // 1 ); new Promise((resolve, reject) => {  reject("出現錯誤"); }).then(  (error) => console.log(error) // 出現錯誤 ); 複製代碼

由於 Promise 的對象時當即建立的,因此在 resolve 和 reject 函數以前的操做都會當即執行:數組

new Promise((resolve, reject) => {
 console.log(2);  resolve(1); }).then(  (value) => console.log(value) // 2 1 ); 複製代碼

Promise 執行 then 方法後會返回回來一個新的 Promise 對象,因此能夠採用鏈式調用。promise

new Promise((resolve) => {
 resolve(1); })  .then((value) => value + 1)  .then((value) => console.log(value)); // 2 複製代碼

第一個 then 函數的返回值,能夠做爲參數傳給第二個 then 函數。若是第一個 then 函數返回的依舊是一個 Promise 對象呢?便是一個 Promise 封裝的異步操做:markdown

new Promise((resolve) => {
 resolve(1); })  .then(  (value) =>  new Promise((resolve) => {  resolve(3);  })  )  .then((value) => console.log(value)); // 3 複製代碼

此時第二個 then 函數傳入的參數,即爲第一個 then 函數返回的 Promise 對象的 resolved 狀態下傳遞的值。也能夠說只有第一個 then 返回的 Promise 執行狀態成功時,第二個 then 函數纔會執行。異步

2.二、catch()

除了 then 函數外,在 Promise 原型上還有一個 catch 函數,此函數時當 Promise 內部異步出現錯誤的時候即爲 rejected 狀態時,才執行。

new Promise((resolve, reject)=>{
 throw new Error('test') }).catch(err=>console.log(err)) // Error: test 等同於: new Promise((resolve, reject)=>{  throw new Error('test') }).then(null, err=>console.log(err)) // Error: test 複製代碼

當 then 第二個參數和 catch 函數同時存在時,將不會執行 catch 函數:

new Promise((resolve, reject) => {
 throw new Error("test"); })  .then(null, (err) => console.log(err)) // Error: test  .catch((err) => console.log(err)); 複製代碼

那此時的 catch 捕獲的是哪一個 Promise 的錯誤呢?捕獲的是前一個 Promise 的錯誤,即 then 函數返回回來的 Promise 錯誤:

new Promise((resolve, reject) => {
 throw new Error("test"); })  .then(null, (err) => {  throw new Error("test1");  })  .catch((err) => console.log(err)); // Error: test1 複製代碼

Promise 的錯誤有一種相似冒泡機制,當 catch 以前沒有沒有任何函數截獲錯誤,那終究會被 catch 截獲。

new Promise((resolve, reject) => {
 throw new Error("test"); })  .then()  .catch((err) => console.log(err)); // Error: test 複製代碼

只要 catch 前任何一個 Promise 報錯,那終究會被 catch 截獲。 一般狀況下,不建議使用 then 函數的第二個參數來進行錯誤的捕獲,如上所說的 catch 寫法能夠捕獲前面 then 方法執行中的錯誤,也更接近同步的寫法(try/catch)。所以,建議老是使用 catch()方法,而不使用 then()方法的第二個參數。 then()返回一個新的 Promise 對象,catch()一樣返回一個 Promise 對象,一樣可使用鏈式調用:

new Promise((resolve, reject) => {
 throw new Error("test"); })  .then()  .catch((err) => console.log(err)) // Error: test  .then(() => console.log(2)); // 2 複製代碼

當 catch 捕獲完錯誤後,會接着執行下面的 then 方法,當沒有錯誤拋出時,則會跳過 catch,直接執行後面的 then 方法。可是以後的 Promise 出現錯誤時,以前的 catch 就捕獲不到了。

new Promise((resolve, reject) => {
 throw new Error("test"); })  .then()  .catch((err) => {  throw new Error("test1");  }) // Error: test  .then(() => console.log(2)); 複製代碼

catch 後面能夠鏈式調用 then 方法,一樣也能夠調用 catch 方法,後面的 catch 方法是接收前一個 catch 方法所拋出的錯誤。

2.三、finally

在 ES8 中新加入了一個方法,即 finally,此方法不一樣於 then 和 catch,它不跟蹤與 Promise 對象狀態的改變,即無論 Promise 的最終狀態如何,都會執行這個方法,同時 finally 不一樣於 then 和 catch 地方就是,它不接受任何參數。

new Promise((resolve) => {
 resolve(1); })  .then((value) => console.log(value)) // 1  .finally(() => console.log(2)); // 2  new Promise(() => {  throw new Error("test"); })  .catch((err) => console.log(err)) // Error test  .finally(() => console.log(2)); // 2 複製代碼

finally 一樣返回一個新的 Promise 對象,用法和以前的 then 和 catch 同樣,這塊就不作過多的講解了。

三、其餘 API

除了上述 Promise 原型上的方法外,Promise 還有不少其餘的 API。

3.一、 Promise.all

經過字面意思就能看出來,這個方法是‘所有’意思,因而可知能夠接受多個 Promise 對象。 該方法接受具備 Iterator 接口而且每一個成員都是 Promise 實例的參數,並返回一個新的 Promise 對象。

let p = Promise.all([
 new Promise((resolve) => {  resolve(1);  }),  new Promise((resolve) => {  resolve(2);  }),  new Promise((resolve) => {  resolve(3);  }), ]); 複製代碼

而且,只有當接受的參數中全部成員的狀態都爲 resolved 的時候,p 的狀態纔會爲 resolved,若是有一個成員的狀態爲 rejected,那 p 的狀態就爲 rejected。 當全部成員的狀態均爲 resolved 的時候,會將每一個成員 resolved 狀態下的值拼成數組傳遞給 p 的回調函數。

let p = Promise.all([
 new Promise((resolve) => {  resolve(1);  }),  new Promise((resolve) => {  resolve(2);  }),  new Promise((resolve) => {  resolve(3);  }), ]); p.then((result) => console.log(result)); // [1, 2, 3] 複製代碼

當有一個成員的狀態爲 rejected 的時候,則會將第一個 rejected 狀態的值返給 p 的 catch 方法。

let p = Promise.all([
 new Promise((resolve) => {  resolve(1);  }),  new Promise((resolve, rejecct) => {  rejecct(2);  }),  new Promise((resolve) => {  resolve(3);  }), ]); p.catch((err) => console.log(err)); 複製代碼

若是有一個成員爲 rejected 狀態,而且自身調用了 catch 方法,那將不會走 p 對象的 catch 方法,這一點要注意。

3.二、 Promise.race

race 翻譯成中文是競賽的意思,他表示多個 Promise 對象,哪一個成員率先改變狀態,那 Promise.race 返回的 Promise 對象的狀態變爲何,並將值轉遞給 p 的回調函數,它和 Promise.all 接收的參數同樣。

let p = Promise.race([
 new Promise((resolve) => {  setTimeout(() => resolve(1), 100);  }),  new Promise((resolve) => {  setTimeout(() => resolve(2), 50);  }),  new Promise((resolve) => {  setTimeout(() => resolve(3), 200);  }), ]); p.then((result) => console.log(result)); // 2  let p = Promise.race([  new Promise((resolve) => {  setTimeout(() => resolve(1), 100);  }),  new Promise((resolve, reject) => {  setTimeout(() => reject(2), 50);  }),  new Promise((resolve) => {  setTimeout(() => resolve(3), 200);  }), ]); p.catch((err) => console.log(err)); // 2 複製代碼

3.三、Promise.allSettled

該方法是 ES2020 新加入的,和 all 同樣,返回一個新的 Promise 對象,接收一組 Promise 對象,可是和 all 區別的是,當無論每一個成員的 Promise 是什麼狀態,只要執行結束,則返回的 Promise 對象就會執行結束。

let p = Promise.allSettled([
 new Promise((resolve) => {  resolve(1);  }),  new Promise((resolve, rejecct) => {  rejecct(2);  }),  new Promise((resolve) => {  resolve(3);  }), ]); p.then((value) => console.log(JSON.stringify(value))); // [{status:"fulfilled",value:1},{status:"rejected",reason:2},{status:"fulfilled",value:3}] 複製代碼

有時候異步請求並不在乎是否可以成功,這個時候這個方法就很符合場景了,而且返回一個數組,數組中每一個對象有兩個狀態,一個是 fulfilled,另外一個是 rejected,返回以後能夠進行篩選,查看錯誤信息。

3.四、Promise.resolve

將一個對象轉化爲一個 Promise 對象。

Promise.resolve("foo");
等價於; new Promise((resolve) => resolve("foo")); 複製代碼

當 Promise.resolve 的參數是一個 Promise 實例時,原封不動的返回這個實例:

let p = new Promsie((resolve) => resolve(1));
let p1 = Promise.resolve(p); p === p1; // true 複製代碼

當參數是一個 thenable 對象時,即含有 then 方法的對象時,會返回一個 Promise 對象,並當即執行 then 方法。

let thenable = {
 then: function (resolve, reject) {  resolve(1);  }, }; let p = Promise.resolve(thenable); p.then((value) => console.log(value)); // 1 複製代碼

當參數是否是一個 thenable 對象時,因爲參數不是一個異步的,因此當 Promise.resolve 後,直接的狀態就是 resolved 的狀態,因此 then 後就會輸出原值。

1. 參數是個普通對象
let obj = {  name: '1' } let p = Promise.resolve(obj); p.then(value=>console.log(value)) // {name: '1'} 2. 參數是個基本數據類型 let num = '1'; let p = Promise.resolve(num); p.then(value=>console.log(value)) // 1 複製代碼

當不傳參數的時候,返回的就是一個帶有 resolved 狀態的 Promise 對象。

3.五、Promise.reject

返回一個狀態爲 rejected 的 Promise 對象,傳入的參數做爲錯誤信息做爲後續方法傳遞的參數。

let num = "1";
let p = Promise.reject(num); p.then(null, (err) => console.log(err)); // 1 複製代碼

當參數是 thenable 對象時,返回的不是 error 信息而是 thenable 對象。

let thenable = {
 then: function (resolve, reject) {  reject(1);  }, }; let p = Promise.reject(thenable); p.then(null, (err) => console.log(err === thenable)); // true 複製代碼

後語

相關文章:

以爲還能夠的,麻煩走的時候能給點個贊,你們一塊兒學習和探討!

還能夠關注個人博客但願能給個人github上點個Start,小夥伴們必定會發現一個問題,個人全部用戶名幾乎都與番茄有關,由於我真的很喜歡吃番茄❤️!!!

想跟車不迷路的小夥還但願能夠關注公衆號 前端老番茄 或者掃一掃下面的二維碼👇👇👇。

我是一個編程界的小學生,您的鼓勵是我不斷前進的動力,😄但願能一塊兒加油前進。

本文使用 mdnice 排版

相關文章
相關標籤/搜索