一. Promise基礎
1. 背景
在Promise出現之前,異步任務的處理方式,以發送請求為例,響應成功和失敗返回不同的信息,這個時候我們需要自己封裝回調方法。但這樣有很大的弊端:
(1). 在自己封裝的方法里,必須使用自己設計的callBack名稱,不能寫錯名字
(2). 別人如果使用我們封裝的方法,必須看文檔或者源碼,因為他不知道successCallBack到底是成功的回調還是失敗的回調,否則不知道怎么拿到返回值哦.
代碼分享-自己封裝回調
function requesetData(url, successCallBack, failureCallBack) { // 模擬異步的網絡請求 setTimeout(() => { // url傳入ypf,響應成功,否則失敗 if (url == "ypf") { successCallBack("請求成功"); } else { failureCallBack("請求失敗"); } }, 2000); } // 調用 requesetData( "ypf", res => { console.log("res:" + res); }, err => { console.log("error:" + err); } );
2. 什么是Promise?
Promise是ES6新增的一個類,可以翻譯為承諾(許諾、契約等),Promise的規范是固定,使用者可以按照該固定的規范進行回調的獲取。
當我們new創建1個Promise對象時候,需要傳入一個回調函數(稱之為executor)
(1).這個回調函數會被立即執行,並且給傳入另外兩個回調函數resolve、reject;
(2).當我們調用resolve回調函數時,表示回調成功,會執行Promise對象的then方法中的【第一個參數位置傳入】的回調函數;
(3).當我們調用reject回調函數時,表示回調失敗,會執行Promise對象的then方法中的【第二個參數位置傳入】的回調函數,或者catch方法傳入的回調函數;
代碼分享-promise基本用法
{ console.log("--------2.1 Promise基本用法-----------"); const resultPromise1 = new Promise((resolve, reject) => { resolve("成功了"); }); resultPromise1.then(res => console.log(res)); const resultPromise2 = new Promise((resolve, reject) => { reject("失敗了"); }); // 失敗回調獲取-寫法1 resultPromise2.then(null, err => console.log(err)); // 失敗回調獲取-寫法2 resultPromise2.then(null, null).catch(err => console.log(err)); }
案例:改造1中的異步請求的封裝
{ console.log("--------2.2 Promise用於異步請求函數封裝-----------"); function requesetData(data) { return new Promise((resolve, reject) => { // 模擬異步的網絡請求 setTimeout(() => { if (data == "ypf") { resolve("獲取成功111111"); } else { reject("獲取失敗22222"); } }, 2000); }); } // 調用-寫法1 const resultPromise = requesetData("ypf"); resultPromise.then( res => { console.log(res); }, err => { console.log(err); } ); // 調用-寫法2 const resultPromise2 = requesetData("lmr"); resultPromise2 .then(res => { console.log(res); }) .catch(err => { console.log(err); }); }
3. Promise的三種狀態
(1). 待定(pending): 初始狀態,既沒有被兌現,也沒有被拒絕;當執行executor中的代碼時,處於該狀態;
(2). 已兌現(fulfilled): 意味着操作成功完成;執行了resolve時,處於該狀態; (這個狀態通常也叫做 resolved,更容易被記住)
(3). 已拒絕(rejected): 意味着操作失敗;執行了reject時,處於該狀態;
注:一旦狀態被確定下來,Promise的狀態會被 鎖死,該Promise的狀態是不可更改的。
在我們調用resolve的時候,如果resolve傳入的值本身不是一個Promise,那么會將該Promise的狀態變成 兌現(fulfilled); 在之后我們去調用reject時,已經不會有任何的響應了(並不是這行代碼不會執行,而是無法改變Promise狀態)
代碼分享:
{ console.log("----------3. Promise的三種狀態----------"); /* const myPromise = new Promise((resolve, reject) => { resolve("333"); }); myPromise.then(res => {}).catch(err => {}); */ // 下面代碼是立即執行的,等價於上面代碼 new Promise((resolve, reject) => { // 狀態1 該進來的時候是 pending狀態, 待定狀態 console.log("------------------------"); resolve(11); //狀態2 fulfilled 成功狀態 reject(22); //狀態3 rejected 失敗狀態 (注:不生效了,因為前面resolve已經確定狀態了,就鎖定了,不能再改了) }) .then(res => { console.log(res); }) .catch(err => { console.log(err); }); }
4. resolve參數【重點】
(1). 如果resolve傳入一個普通的值或者對象,那么這個值會作為then回調的參數。
(2). 如果resolve中傳入的是另外一個Promise,那么這個新Promise會決定原外層Promise的狀態.
A. 內層Promise調用resolve(fulfilled狀態),那么就進入then中的回調成功的位置
B. 內層Promise調用reject(rejected狀態),那么就進入then中回調失敗的位置 或者 catch 中
(3). resolve中傳入的是一個對象,並且這個對象有實現then方法,那么會執行該then方法,並且根據then方法的結果來決定Promise的狀態
A. then中調用resolve,那么最終進入then中的回調成功的位置
B. then中調用reject,那么最終就進入then中回調失敗的位置 或者 catch 中
代碼分享:
// 4.1 傳入普通值 { console.log("--------------4.1 傳入普通值------------------"); const promiseResult = new Promise((resolve, reject) => { resolve("ypf1"); }); promiseResult.then(res => console.log(res)); //ypf1 } // 4.2 傳入Promise { console.log("-------------- 4.2 傳入Promise------------------"); const promiseResult = new Promise((resolve, reject) => { resolve( new Promise((resolve2, reject2) => { reject2("ypf2"); }) ); }); promiseResult.then(null, err => console.log(err)); //ypf2 } // 4.3 傳入對象,且對象有實現then方法 { console.log("------4.3 傳入對象,且對象有實現then方法-----------"); const promiseResult = new Promise((resolve, reject) => { resolve({ then: function (resolve2, reject2) { reject2("ypf3"); }, }); }); promiseResult.then(null, err => console.log(err)); //ypf3 }
二. Promise對象方法
1. then方法
(1). then方法接收兩個參數:
A.fulfilled的回調函數:當狀態變成fulfilled時會回調的函數;
B.reject的回調函數:當狀態變成reject時會回調的函數;
注:reject的回調函數等價於catch方法中調用
// 1.1 then的兩個參數 { console.log("---------- 1.1 then的兩個參數--------------"); const promiseResult = new Promise((resolve, reject) => { reject("出錯了"); }); // promiseResult.then(null, err => console.log(err)); // 等價於 promiseResult.catch(err => console.log(err)); }
(2).一個Promise的then方法是可以被多次調用的
代碼分享:
{ console.log("--------1.2 then方法是可以被多次調用的-----------"); const promiseResult = new Promise((resolve, reject) => { resolve("成功了"); }); // 下面代碼都輸出 promiseResult.then(res => console.log(`res1:${res}`)); promiseResult.then(res => console.log(`res2:${res}`)); promiseResult.then(res => console.log(`res3:${res}`)); }
(3).返回值問題【重點】
then方法本身也是有返回值的, 它的返回值是Promise.
A. 如果我們返回的是一個普通值(數值/字符串/普通對象/undefined), 那么這個普通的值被作為一個新的Promise的resolve值
B. 如果我們返回的是一個Promise,那么當前的Promise的狀態會由傳入的Promise的狀態來決定 相當於狀態進行了移交 【原理同 ‘resolve參數’】
C. 如果返回的是一個對象, 並且該對象實現了then方法,則由then方法中的狀態進行決定哦 【原理同 ‘resolve參數’】
D. 當then方法拋出一個異常時,那么它處於reject狀態; 【詳解下面catch方法的測試】
代碼分享:
// 1.3.1 返回值是普通值 { console.log("-----1.3.1 返回值是普通值--------"); const promiseResult = new Promise((resolve, reject) => { resolve("hahaha"); }); promiseResult .then(res => { return "ypf1"; //不寫返回值,返回的是undefined }) .then(res => console.log(res)); //輸出ypf1 } // 1.3.2 返回值是Promise { console.log("-----1.3.2 返回值是Promise--------"); const promiseResult = new Promise((resolve, reject) => { resolve("hahaha"); }); promiseResult .then(res => { return new Promise((resolve, reject) => { reject("出錯了"); //進行了狀態移交,狀態變為rejected,所以在后面then的第二個參數中接受 }); }) .then(null, err => console.log(err)); //輸出ypf1 } // 1.3.3 返回值是對象,里面實現了then方法 { console.log("-----1.3.3 返回值是對象,里面實現了then方法-------"); const promiseResult = new Promise((resolve, reject) => { resolve("hahaha"); }); promiseResult .then(res => { return { then: function (resolve, reject) { reject("出錯了"); }, }; }) .then(null, err => console.log(err)); //輸出ypf1 }
2. catch方法
(1). 當then方法拋出一個異常時,那么它處於rejected狀態 →→ 就會進入后面的catch 【原理 見上述返回值問題】
// 2.1. then中拋出異常,直接進入catch { console.log("--------2.1. then中拋出異常,直接進入catch---------------"); const promise = new Promise((resolve, reject) => { throw new Error("rejected status"); }); promise.catch(err => { console.log("err:", err); }); }
(2). catch的多次調用:一個Promise的catch方法是可以被多次調用的
// 2.2 catch的多次調用 { console.log("------- 2.2 catch的多次調用-------------"); const promise = new Promise((resolve, reject) => { reject("失敗了"); }); // 下面的代碼都會被執行 promise.catch(err => console.log("err1:", err)); promise.catch(err => console.log("err2:", err)); promise.catch(err => console.log("err3:", err)); }
(3). catch的返回值:catch方法也是會返回一個Promise對象的,所以catch方法后面我們可以繼續調用then方法或者catch方法
A. 返回普通值(數值/字符串/普通對象/undefined),進入下一個then
B. 拋出異常,進入下一個catch
代碼分享:
{ console.log("-------2.3 catch的返回值------------"); const promise = new Promise((resolve, reject) => { reject("ypf1"); }); promise .then(res => { console.log("res:", res); }) .catch(err => { console.log("err:", err); //先執行這句話 【err: ypf1】 return "ypf2"; }) .then(res => { console.log("res result:", res); //再執行這句話 【res result: ypf2】 }) .catch(err => { console.log("err result:", err); }); }
3. finally方法
(1).finally是在ES9(ES2018)中新增的一個特性:表示無論Promise對象無論變成fulfilled還是reject狀態,最終都會被執行的代碼。
(2).finally方法是不接收參數的,因為無論前面是fulfilled狀態,還是reject狀態,它都會執行。
代碼分享:
{ console.log("3.-----------------3. finally方法--------------------------"); const promise = new Promise((resolve, reject) => { resolve("resolve message"); // reject("reject message"); }); promise .then(res => { console.log("res:", res); }) .catch(err => { console.log("err:", err); }) .finally(() => { console.log("finally code execute"); }); }
三. Promise類方法
1. resolve方法
(1). Promise.resolve的用法相當於new Promise,並且執行resolve操作.
(2). resolve方法參數問題:
A:參數是一個普通的值或者對象
B:參數本身是Promise
C:參數是一個thenable
PS:這里和前面講的resolve參數完全一樣哦。【詳見前面 4.resolve參數】
{ console.log("---------------1. resolve方法-------------------"); const promiseResult = Promise.resolve("ok"); // 等價於上面的話 const promiseResult2 = new Promise((resolve, reject) => { resolve("ok"); }); // 這里演示了 Promise.resolve("ok");傳字符串的情況,至於傳入promise和含then方法的對象,不再演示了 promiseResult.then(res => console.log(res)); }
2. reject方法
(1). Promise.reject的用法相當於new Promise,只是會調用reject操作
特別注意:Promise.reject傳入的參數無論是什么形態,都會直接作為reject狀態的參數傳遞到catch的
代碼分享:
{ console.log("--------------2. reject方法-------------------"); const promiseResult = Promise.reject("error1了"); // 等價於上面的話 // const promiseResult2 = new Promise((resolve, reject) => { // reject("error2了"); // }); promiseResult.catch(err => console.log(err)); } // 注意: 無論傳入什么值都是一樣的(這里不分情況,都是進入catch) { console.log("--- 注意: 無論傳入什么值這里不分情況,都是進入catch"); const promiseResult = Promise.reject( new Promise((resolve, reject) => { resolve("okokok"); }) ); // 輸出:Promise { 'okokok' } promiseResult.catch(err => console.log(err)); }
3. all方法
(1). 它的作用是將多個Promise包裹在一起形成一個新的Promise;
(2). 新的Promise狀態由包裹的所有Promise共同決定:
A. 當所有的Promise狀態變成fulfilled狀態時(resolved),新的Promise狀態為fulfilled,並且會將所有Promise的返回值組成一個數組;
B. 當有一個Promise狀態為reject時,新的Promise狀態為reject,並且會將第一個reject的返回值作為參數;
代碼分享:
{ console.log("------------- 3. all方法-------------------"); const p1 = new Promise((resolve, reject) => { setTimeout(() => resolve(11111), 1000); }); const p2 = new Promise((resolve, reject) => { setTimeout(() => resolve(22222), 2000); }); const p3 = new Promise((resolve, reject) => { setTimeout(() => reject(3333), 3000); }); // 測試1-全部promise都變為fulfilled Promise.all([p2, p1, "test1"]) .then(res => console.log("res:" + res)) //res:22222,11111,test1 (這里是按照傳入的順序進行返回的) .catch(err => console.log("err:" + err)); // 測試2-其中1個變為rejected Promise.all([p2, p1, p3]) .then(res => console.log("res:" + res)) .catch(err => console.log("err:" + err)); //err:3333 }
4. allSettled方法
(1). 背景
all方法有一個缺陷:當有其中一個Promise變成reject狀態時,新Promise就會立即變成對應的reject狀態。
那么對於resolved的,以及依然處於pending狀態的Promise,我們是獲取不到對應的結果的;
(2). 在ES11(ES2020)中,添加了新的API Promise.allSettled:該方法會在所有的Promise都有結果(settled),無論是fulfilled,還是reject時,才會有最終的狀態;
並且這個Promise的結果一定是fulfilled的;
(3).返回值:
allSettled的結果是一個數組,數組中存放着每一個Promise的結果,並且是對應一個對象的;這個對象中包含status狀態,以及對應的value值;
console.log(item.status, item.value, item.reason);
A.statue:表示狀態, fulfilled 或 rejected
B.value: fulfilled時對應的內容
C.reason: reject時對應的內容
代碼分享:
{ console.log("------------- 4. allSettled方法-------------------"); const p1 = new Promise((resolve, reject) => { setTimeout(() => resolve(11111), 1000); }); const p2 = new Promise((resolve, reject) => { setTimeout(() => resolve(22222), 2000); }); const p3 = new Promise((resolve, reject) => { setTimeout(() => reject(3333), 3000); }); // 測試1-全部promise都有結果 Promise.allSettled([p1, p2, p3]) .then(res => { for (const item of res) { console.log(item.status, item.value, item.reason); } }) .catch(err => console.log("err:" + err)); }
運行結果:

5. race方法
如果有一個Promise有了結果,我們就希望決定最終新Promise的狀態,那么可以使用race方法:
race是競技、競賽的意思,表示多個Promise相互競爭,誰先有結果,那么就使用誰的結果;
代碼分享:
{ console.log("------------- 5. race方法-------------------"); const p1 = new Promise((resolve, reject) => { setTimeout(() => resolve(11111), 1000); }); const p2 = new Promise((resolve, reject) => { setTimeout(() => resolve(22222), 2000); }); const p3 = new Promise((resolve, reject) => { setTimeout(() => reject(3333), 3000); }); Promise.race([p1, p2, p3]) .then(res => { console.log("res:", res); //上面p1先執行完,所以狀態是fulfilled,輸出結果為:11111 }) .catch(err => { console.log("err:", err); }); }
6. any方法
any方法是ES12中新增的方法,和race方法是類似的:
(1). any方法會等到一個fulfilled狀態,才會決定新Promise的狀態;
(2). 如果所有的Promise都是reject的,那么也會等到所有的Promise都變成rejected狀態;
注: 如果所有的Promise都是reject的,那么會報一個AggregateError的錯誤。
代碼分享:
{ console.log("------------- 5. race方法-------------------"); const p1 = new Promise((resolve, reject) => { setTimeout(() => resolve(11111), 1000); }); const p2 = new Promise((resolve, reject) => { setTimeout(() => resolve(22222), 2000); }); const p3 = new Promise((resolve, reject) => { setTimeout(() => reject(3333), 3000); }); Promise.any([p1, p2, p3]) .then(res => { console.log("res:", res); //上面p1先執行完,所以狀態是fulfilled }) .catch(err => { console.log("err:", err); }); }
!
- 作 者 : Yaopengfei(姚鵬飛)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 聲 明1 : 如有錯誤,歡迎討論,請勿謾罵^_^。
- 聲 明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。
