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