摘要:回調方法立即得到結果的,其回調函數依然會晚于本輪事件執行。實例方法該方法可傳入兩個,分別對應成功失敗時的回調函數。鏈式中,后者的狀態取決于前者成功失敗的回調函數中返回的結果。依次打印出用于指定發生錯誤時的回調函數,等價于。
Promise是異步編程的解決方案之一,相比傳統的回調和事件機制更為合理和強大。
1 場景舉例某天,突發奇想,發了封郵件給木匠師傅,定制一個如此這般的家具。
木匠有求必應,即是說,郵件一旦發出就得到了他的承諾(Promise):在下一定盡力。
郵件中規定好了結果的通知方式:
成功了,直接將家具(res)郵遞(resolve)過來。
失敗了,直接將失敗的信息(err)發郵件(reject)過來。
let P = new Promise((resolve, reject) => { if (/*最終的結果*/) { resolve("家具"); // 成功,直接郵遞家具。 } else { reject("失敗的原因"); // 失敗,發郵件告知失敗原因。 } });
郵件發出等價于得到木匠的承諾P,之后,能做的只有等待(then)。
P.then(res => { console.log("成功,收到家具。此刻心情:開心。"); }, err => { console.log("失敗,收到原因。此刻心情:失落。"); });2 行為特征 2.1 狀態
每個Promise有三種狀態:進行中(pending)、已成功(resolved)和已失敗(rejected)。
創建即進入pending狀態,在傳入方法中一旦調用了resolve/reject方法,最終狀態便變成resolved/rejected。
一旦變成結果狀態,即更改成resolved/rejected,狀態便被冷凍,不能再被更改。
狀態容器
Promise實質是個狀態容器。
得到結果狀態后,任何時候都可以訪問到此狀態。
這與事件訂閱通知不同,如果訂閱發生在通知之后,訂閱是不起作用的。
狀態不可控
一旦創建Promise,便會立刻執行,無法取消。
處于pending狀態時,無法得知進程具體的信息,比如完成百分比(雖然可以自行設置回調進行通知)。
失敗的狀態
成功的狀態只能由resolve方法轉成。
失敗的狀態可以由reject方法轉成,也可以由拋出錯誤間接轉成。
三者都會正常的打印出失敗的信息。 new Promise((resolve, reject) => { reject("error"); }).catch(console.log); // error new Promise((resolve, reject) => { a; }).catch(console.log); // ReferenceError: a is not defined new Promise((resolve, reject) => { throw "error"; }).catch(console.log); // Error: error
錯誤的報告機制
如果失敗狀態沒有接收失敗的回調函數接收,Promise會拋出錯誤。
這里的拋出錯誤,僅僅是在控制臺顯示之類的提示,不會終止程序的進程。
先打印出 "err" ,再報錯。 new Promise((resolve, reject) => { reject(); }); new Promise((resolve, reject) => { reject("err"); }).then(() => {}, console.log);
一旦Promise設置了失敗回調函數,即便是代碼執行錯誤,也會自行消化,不外報。
雖然 a 未被定義,但全程安靜,無槽點。 new Promise((resolve, reject) => { a; }).then(() => {}, () => {});2.2 執行順序
傳入方法
創建Promise的同時也會執行傳入方法。
傳入方法不會因為調用了resolve/reject便終止執行,所以更優的方式是retrun resolve/reject。
打印出 1 2 。 new Promise((resolve, reject) => { console.log(1); resolve(); console.log(2); });
回調方法
立即得到結果的Promise,其回調函數依然會晚于本輪事件執行。
這種后執行不同于setTimeout的將執行函數push到執行棧,而是將執行函數放到本輪的末尾。
得到的結果是:1 2 3 4 5 6 。 console.log(1); let p = new Promise((resolve, reject) => { console.log(2); resolve(); }); setTimeout(() => { console.log(5); }); p.then(function() { console.log(4); }); setTimeout(() => { console.log(6); }); console.log(3);2.3 結果參數
傳入reject的參數,一般是字符串或Error實例,表示拋出的錯誤。
傳入resolve的參數,一般是相應的JSON數據等,表示得到的數據。
傳入resolve的參數,還可以是另一個Promise實例。
這時,只有當內層的Promise結束后,外層的Promise才會結束。
過兩秒后,打印出 2000 。 new Promise((resolve, reject) => { resolve(createPromise()); }).then(console.log); function createPromise() { return new Promise((resolve, reject) => { setTimeout(() => { resolve(2000); }, 2000); }); }
在這種情況下,如果內層失敗,并不等于傳遞Error實例給resolve不同。
前者是內層Promise拋出了錯誤將被外層捕獲,后者僅僅是參數為一個Error實例。
內層失敗的信息,被外層捕獲。過兩秒,打印出 "2" 2000 。 new Promise((resolve, reject) => { resolve(createPromise()); }).then(res => { console.log("1", res); }, err => { console.log("2", err); }); function createPromise() { return new Promise((resolve, reject) => { setTimeout(() => { reject(2000); }, 2000); }); }3 實例方法 3.1 then()
該方法可傳入兩個,分別對應成功/失敗時的回調函數。
該方法返回的是一個新的Promise對象,這也是可以使用鏈式(.then.then...)的原因。
let p1 = new Promise(resolve => resolve(2000)); let p2 = p1.then(() => {}, () => {}); console.log(p1 === p2); // false
return
鏈式中,后者的狀態取決于前者(成功/失敗)的回調函數中返回(return)的結果。
如果沒有返回,相當返回一個成功的狀態,值為undefined。
如果返回為Promise對象,后者的狀態由該對象的最終狀態決定。
如果返回為非Promise對象的數據,相當返回一個成功的狀態,值為此數據。
如果前者執行時拋出了錯誤,相當是返回一個失敗的狀態,值為此錯誤。
依次打印出: 1 res 2000 2 res undefined 3 res 3000 4 err 4000 new Promise(resolve => resolve(2000)) .then(res => { console.log("1 res", res); }) .then(res => { console.log("2 res", res); return 3000; }) .then(res => { console.log("3 res", res); return new Promise((resolve, reject) => { reject(4000); }); }) .then(console.log, err => { console.log("4 err", err); });
狀態的傳遞
在鏈式中,如果前者的狀態沒有被后者捕獲,會一直(像)冒泡到被捕獲為止。
狀態被捕獲后便消失,這之后的的狀態由當前then返回的狀態決定,之后重復。
依次打印出: 2 res 2000 3 res 3000 new Promise(resolve => resolve(2000)) .then(null, err => { console.log("1 err", err); }) .then(res => { console.log("2 res", res); return 3000; }) .then(res => { console.log("3 res", res); });3.2 catch()
用于指定發生錯誤時的回調函數,等價于:.then(null, callback)。
其表現與then一致,比如返回新的Promise,狀態的繼承和傳遞等等。
一般推薦使用catch而不是then的第二個方法接收錯誤。
因為catch可以捕獲then自身的錯誤,也更接近同步的寫法(try/catch)。
new Promise(() => {}) .then(() => { ... }) .catch(() => { ... });3.3 finally()
用于Promise處理結束后的收尾工作。
傳入其的回調函數不會接受任何參數,意味著沒有辦法知道Promise的結果。
這也正表明,finally里面的操作與狀態無關,不依賴Promise的處理結果。
其本質和catch一樣,也是then方法的變種。
不過其僅僅是狀態的傳遞者,只會返回原狀態,不會接收狀態和創建新的狀態。
p.finally(() => { // codes... }); --- 等價于 p.then(res => { // codes... return res; // 將原成功狀態返回 }, err => { // codes... throw err; // 將原失敗狀態返回 });
示例
在請求數據時,我們會顯示加載圖案,請求完成后無論結果都要隱藏此圖案。
一般,一個完整的 Promise 的結構會如下。 showLoading = true; new Promise((resolve, reject) => { // 請求... }) .then(res => { // 成功處理... }) .catch(err => { // 失敗處理... }) .finally(() => { // 重置一些狀態... showLoading = false; });4 靜態方法 4.1 resolve()
此方法直接返回一個狀態為resolved,值為其參數的Promise。
Promise.resolve(res); --- 等價于 new Promise(resolve => resolve(res));4.2 reject()
此方法直接返回一個狀態為rejected,值為其參數的Promise。
Promise.reject(res); --- 等價于 new Promise((resolve, reject) => reject(res));4.3 all()
此方法用于將多個Promise實例,包裝成一個新的Promise實例。
其參數為一個數組,每一項應為Promise實例(不是則會使用Promise.resolve進行轉化)。
新Promise的狀態取決于傳入數組中的每一項的最終狀態。
如果有一項狀態變成rejected,新實例則為rejected,值為該項的返回值。
如果全部項都變成了resolved,新實例則為resolved,值為包含每一項返回值的數組。
三秒后,打印出:[1, 2, 3]。 let pArr = [1, 2, 3].map(createPromise); Promise.all(pArr).then(console.log); function createPromise(num) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(num) }, num * 1000); }); }4.4 race()
此方法與all()基本相同,傳入的參數也是一個Promise數組。
不同的是,新Promise的最終狀態是由數組中第一個狀態改變的項(成功或失敗)決定的。
一秒后,打印出 1 。 let pArr = [1, 2, 3].map(createPromise); Promise.race(pArr).then(console.log); function createPromise(num) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(num) }, num * 1000); }); }5 混合實戰
在實際項目中,有時需要處理多個相互關聯的異步腳本(多為數據請求)。
ES6之后async函數應該是最靈活方便的途徑,Promise在其中扮演基石的角色。
不過在這一小節,依舊會以Promise作為主要的解決辦法進行分析。
這里是下面需要用到的共同方法。
// 創建異步。 function createPromise(name) { return new Promise(resolve => { setTimeout(() => resolve({ [name]: `Data form ${name}` }), 1000); }); } // 異步 A, B, C。 function A(param) { return createPromise("A"); } function B(param) { return createPromise("B"); } function C(param) { return createPromise("C"); } // 并發處理多個獨立的異步請求。 function dealIndependentRequests(qArr, callback) { return new Promise((resolve, reject) => { let done = false; let resData = []; let leftNum = qArr.length; qArr.forEach((q, i) => { Promise.resolve(q).then(res => { !done && dealRequest(res, i, true); }).catch(err => { !done && dealRequest(err, i, false); }); }); function dealRequest(res, index, isSuccess) { if (callback) { done = callback(resData, res, index, isSuccess); } else { resData[index] = { res: res, isSuccess: isSuccess }; } if ( done || !(--leftNum) ) resolve(resData); } }); }5.1
5.1.1
有三個請求數據的異步:A, B, C。
最終的數據必須同時結合三者的數據計算得出。
基于要求,直接使用Promise.all進行并發請求,等到所有信息到齊后結束。
大概一秒后,打印出:Get all data: [{...}, {...}, {...}]。 Promise.all([A(), B(), C()]) .then(res => { console.log(`Get all data:`, res); }) .catch(err => { console.error(err); });
5.1.2
有三個請求數據的異步:A, B, C。
最終的數據必須同時結合A, B的數據計算得出,C只是修飾數據。
基于要求,使用Promise.all并發A, B請求,成功后再發C。
如果前者成功,再看C是否成功,之后使用不同方式處理得到最終數據。
大概兩秒后,打印出:[{…}, {…}] {C: "Data form C"}。 Promise.all([A(), B()]) .then(res => { C().then(c => { console.log(res, c); }) .catch(err => { console.log(res); }); }) .catch(err => { console.error(err); });
5.1.3
有三個請求數據的異步:A, B, C。
最終的數據必須基于結合A的數據計算得出,B, C起獨立的修飾作用。
基于要求,與上面的處理基本相同。
不過要在A的回調里同時請求B, C,并使用狀態控制變量控制程序的進程。
大概兩秒后,打印出:End {A: "Data form A"} [{…}, {…}]。 A() .then(res => { dealIndependentRequests([B(), C()]) .then(subs => { console.log("End", res, subs); }) .catch(err => { console.log("End", res); }); }) .catch(err => { console.error(err); });5.2
5.2.1
有三個請求異步:A, B, C。
B的請求需要發送A中的a信息。
C的請求需要發送B中的b信息。
基于要求,必須逐步請求A, B, C,而且前兩者任一出錯則停止。
大概三秒后,打印出:End {C: "Data form C"}。 A() .then(res => { return B(res.a); }) .then(res => { return C(res.b); }) .then(res => { console.log("End", res); }) .catch(err => { console.log(err); });
5.2.2
有三個請求異步:A, B, C。
B的請求需要發送A中的a信息,即便A失敗也需要發送。
C的請求需要發送B中的b信息。
基于要求,與前者基本相同,只是即便A失敗了也會繼續請求。
大概三秒后,打印出:End {C: "Data form C"}。 A() .then(res => { return B(res.a); }) .catch(err => { return B(); }) .then(res => { return C(res.b); }) .then(res => { console.log("End", res); }) .catch(err => { console.log(err); });5.3
5.3.1
有三個請求異步:A, B, C。
需要找出所有異步結果中,包含某值的結果的集合。
基于要求,并發請求所有數據,一一驗證返回符合的結果集。
大概一秒后,打印出:[{B: "Data form B"}] dealIndependentRequests([A(), B(), C()], (data, res) => { if (res.B) data.push(res); return false; }) .then(console.log) .catch(console.log);
5.3.2
有三個請求異步:A, B, C。
只需要找到一個包含某值的結果。
基于要求,還是使用并發請求。
有任一請求符合預期時,結束并返回(暫不涉及取消請求操作)。
大概一秒后,打印出:[{B: "Data form B"}] dealIndependentRequests([A(), B(), C()], (data, res) => { if (res.B) return data.push(res); return false; }) .then(console.log) .catch(console.log);
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/95785.html
摘要:在這里看尤雨溪大神的這篇小短文,非常精簡扼要地介紹了當前常用的。根據尤雨溪大神的說法,的也只是的語法糖而已。對象有三種狀態,,。對象通過和方法來規定異步結束之后的操作正確處理函數錯誤處理函數。方便進行后續的成功處理或者錯誤處理。 最近在寫一個自己的網站的時候(可以觀摩一下~Colors),在無意識中用callback寫了一段嵌套了5重回調函數的可怕的代碼。回過神來的時候被自己嚇了一跳,...
摘要:執行權由此單向穩定的在不同函數中切換。調用函數后,引擎會為其開辟一個獨立的函數執行棧以下簡稱棧。執行權再次回到外部。成功執行完函數,則改變的狀態為成功。執行函數返回的遍歷器對象會繼承函數的原型對象。遇到下一個斷點,交出執行權傳出返回值。 前言 ES6提供了一種新型的異步編程解決方案:Generator函數(以下簡稱G函數)。它不是使用JS現有能力按照一定標準制定出來的東西(Promis...
摘要:本篇概括了中正則表達式新增部分的精華要點最好有的基礎。標志使正則處于模式。關于的字符擴展知識,可查看這里。四字節字符處于模式下的正則,可以正確識別位四字節字符。 本篇概括了ES6中正則表達式新增部分的精華要點(最好有ES5的基礎)。 1 u 標志 使正則處于Unicode模式。 關于ES6的字符擴展知識,可查看這里。 1.1 四字節字符 處于Unicode模式下的正則,可以正確識別3...
摘要:構造函數規定,對象是一個構造函數,用來生成實例。如果中的回調函數拋出一個錯誤,那么返回的將會成為拒絕狀態,并且將拋出的錯誤作為拒絕狀態的回調函數的參數值。 其實想寫 Promise 的使用已經很長時間了。一個是在實際編碼的過程中經常用到,一個是確實有時候小伙伴們在使用時也會遇到一些問題。Promise 也確實是 ES6 中 對于寫 JS 的方式,有著真正最大影響的 API 特性之一。本...
閱讀 1357·2021-10-09 09:44
閱讀 1440·2021-09-28 09:36
閱讀 15927·2021-09-22 15:55
閱讀 1238·2021-09-22 15:45
閱讀 2199·2021-09-02 09:48
閱讀 2783·2019-08-29 17:19
閱讀 2296·2019-08-29 10:54
閱讀 906·2019-08-23 18:40