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