Promise 也是面試高頻問題, 今天我們來看看Promise是什麼, 能做什麼, 怎麼用, 下一期我們自己來模擬一個myPromise 1 Promise 是什麼 我們要學會自己給自己提問, 才能加深我們的理解, 首先 Promise 是一個類, 類(class)是ES6 一個新的概念 ,和ES ...
Promise 也是面試高頻問題, 今天我們來看看Promise是什麼, 能做什麼, 怎麼用, 下一期我們自己來模擬一個myPromise
1 Promise 是什麼
我們要學會自己給自己提問, 才能加深我們的理解, 首先 Promise 是一個類, 類(class)是ES6 一個新的概念 ,和ES5的構造函數相似,
但不完全一樣,
類必須通過 new 操作符 調用 ;
子類通過super() 關鍵字 來產生this , 在super()之前不能使用this ;
類上有靜態屬性 和 靜態方法 , 並且子類可以繼承父類的靜態屬性和靜態方法 ;
Promise 中文 譯 為 承諾 ,承諾就是說出來的話不會改變,
Promise 一旦狀態發生變化後就不會再變了 ,比如:
const p = new Promise((resolve, reject) => { setTimeout(() => { console.log(1); reject('reject'); resolve('resolve') }, 2000); }).then((data) => { console.log(data + '-suc') // 狀態為 resolved 觸發 }, (err) => { console.log(err + '-err') // 狀態為 rejected 觸發 });
結果:兩秒後列印 1 , reject-err
由此可見 , 由於reject() 方法 先把 promise 的 狀態 由 pendding 變為 rejected ,
所以狀態就是rejected , 後面再執行resolve() 方法也沒用了, 狀態只改變一次。
2 Promise 能做什麼
Promise 是為瞭解決非同步回調地獄而出現的, 就是解決那種回調函數中嵌套回調函數的情況。。。。
它可以把這種非同步操作以同步操作的流程表達出來, 一個經典的圖片載入例子:
function loadImg(src) { const p = new Promise((resolve, reject) => { const image = document.createElement('img'); // 創建image標簽 image.src = src; // 開始載入 image.onload = () => { // 載入成功的回調 resolve(image); // 成功把image返回 }; image.onerror = () => { // 載入失敗的回調 reject(new Error('url not found')); // 失敗丟出error } }); return p; } loadImg('https://timgsa.baidu.com/timg' + '?image&quality=80&size=b9999_10000&sec=1554215359089&di=173ff60aee0bcc177d33dced2c88b2ed' + '&imgtype=0&src=http%3A%2F%2Fgss0.baidu.com%2F-fo3dSag_xI4khGko9WTAnF6hhy%2Fzhidao%2Fpic%' + '2Fitem%2F55e736d12f2eb9388d4c2ebad9628535e5dd6f50.jpg').then((data) => { document.body.appendChild(data); // 成功 把 image 標簽 插入 body data.style.width = '800px'; data.style.height = '600px'; }).catch((err) => { console.log(err); // 失敗列印出錯誤 });
效果圖:
把 圖片載入 和 載入成功後的數據處理函數 以 同步操作表達出來 , 而不是把數據處理函數
嵌套在圖片載入成功的函數里。
3 Promise 的其他註意事項
1. resolve函數 和 reject 函數 里 都可以傳參 , reject里一般傳Error的實例,
resolve 里 除了可以傳 正常的值 還可以傳另一個Promise實例,比如:
const P1 = new Promise((resolve, reject) => { setTimeout(() => { reject(new Error('出錯啦')) }, 4000); }); const P2 = new Promise ((resolve, reject) => { setTimeout(() => { resolve(P1) }, 1000); }); P2.then((data) => { console.log(data); }, (err) => { console.log(err) });
結果: 4 秒 列印出 錯誤信息
有的小伙伴可能會有疑問,P2 不是執行resolve函數了嗎, 為什麼列印出的是error 呢 ?
我們重頭開始分析, P1 創建 並 立即 執行 , 4秒後 P1 執行 reject () ;
然後 P2 創建 並立即執行 , 1 秒後 執行 resolve();
1 秒鐘 到了 , P2 執行 resolve(), 但是 P2 的 resolve 返回的是 P1,
那麼後面 P2.then 和 P2 其實就沒什麼 關係 了 , 它 就和 P1 的狀態 綁定 在 一起 了 ,
4 秒鐘 到了 , P1 的狀態 變成 rejected , P2.then 列印 error
我們再來一個 例子驗證 :
const P2 = new Promise ((resolve, reject) => { setTimeout(() => { resolve(new Promise((suc, err) => { })) }, 1000); }); P2.then((data) => { console.log(data); }, (err) => { console.log(err) });
結果 : 控制台沒有任何輸出
為什麼 呢? P2.then 依賴 resolve()里 返回的 Promise 對象 的 狀態 ,
而 我 沒有 調用 任何 改變 其 狀態 的api , 所以 它 的狀態 一直是 pendding ,
所以沒有輸出。
3 Promise 上的靜態方法
① Promise.all()
它接受一個由Promise對象組成的數組, 只有數組中所有Promise的狀態都為resolved時才會調用成功的回調,
只要有一個狀態為rejected, 遇到第一個狀態變為rejected時, 就會調用失敗的回調;
例子:
const p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve(1); }, 2000); }); const p2 = new Promise((resolve, reject) => { resolve(2); }); const p3 = new Promise((resolve, reject) => { resolve(3); }); Promise.all([p1, p2, p3]).then((data) => { console.log(data); // 2 秒後輸出[1, 2, 3] }).catch((error) => { console.log(error) });
兩秒後所有狀態才變為resolved,然後輸出結果。
再看個例子:
const p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve(1); }, 2000); }); const p2 = new Promise((resolve, reject) => { reject(2); }); const p3 = new Promise((resolve, reject) => { reject(3); }); Promise.all([p1, p2, p3]).then((data) => { console.log(data); }).catch((error) => { console.log(error) // 2 });
p2 狀態變為rejected後,Promise.all()的狀態就變為rejected,並列印出2,
上文我們已經說過, promise的狀態只會發生一次改變, 所以即使p3狀態再變為rejected,
Promise.all().catch也不會再輸出了。
② Promise.race() 和 Promise.all()相反, 只要有一個狀態變為resolved, 就會執行then的回調,
並且只列印第一個狀態變化返回的值
例子:
const p1 = new Promise((resolve, reject) => { setTimeout(() => { reject(1); },3000); }); const p2 = new Promise((resolve, reject) => { setTimeout(() => { reject(2); }, 1000) }); const p3 = new Promise((resolve, reject) => { reject(3); }); Promise.race([p1, p2, p3]).then((data) => { console.log(data + 'suc'); }).catch((error) => { console.log(error + 'err'); // 3err });
p3的狀態最先發生改變所以列印3err.
Promise.resolve( ) 返回一個狀態為resolved的promise對象
Promise.reject() 返回一個狀態為rejected 的promise對象
註意事項,鏈式調用then() 如果不顯示return, 隱式返回一個狀態為resolved, 值為undefined的promise對象
例子:
const p3 = new Promise((resolve, reject) => { resolve(3); }).then((data) => { console.log(data) // 3 // return Promise.reject(2); }).then((data) => { console.log(data) // undefined }).then((data) => { console.log(data) // undefined });
我們再顯示return
const p3 = new Promise((resolve, reject) => { resolve(3); }).then((data) => { console.log(data) // 3 return Promise.reject(2); }).then((data) => { console.log(data) // 不輸出 }).then((data) => { console.log(data) // 不輸出 }).catch((err) => { console.log('err' + err) // err2 });
catch也一樣, 隱式返回一個狀態為resolved, 值為undefined的promise對象,
下一期我們來研究一下Promise的源碼