JS Promise axios 請(qǐng)求結(jié)果后面的.then() 是什么意思
Promise 是JS中一種處理異步操作的機(jī)制,在現(xiàn)在的前端代碼中使用頻率很高。Promise 這個(gè)詞可能有點(diǎn)眼生,但你肯定見(jiàn)過(guò) axios.get(...).then(res => {...})
;用于異步請(qǐng)求的 axios 返回的就是一個(gè) Promise 對(duì)象。
平時(shí)一直在代碼中 .then()
.catch()
地寫來(lái)寫去,終于決定要認(rèn)真學(xué)一學(xué)這個(gè) Promise 到底是怎么回事,希望這篇學(xué)習(xí)筆記也能幫到你。
Promise 對(duì)象
一個(gè) Promise 對(duì)象表示一個(gè)異步操作的執(zhí)行結(jié)果,包括狀態(tài)(成功/失敗)和值(成功返回值/錯(cuò)誤原因)。一個(gè) Promise 在創(chuàng)建的時(shí)候異步操作可能還沒(méi)執(zhí)行完成,通過(guò)持有這個(gè) Promise 對(duì)象,可以在未來(lái)異步操作完成的時(shí)候?qū)Y(jié)果進(jìn)行相應(yīng)操作。
Promise 對(duì)象的狀態(tài)
這里有幾個(gè)常用的名詞,用來(lái)表示 Promise 對(duì)象當(dāng)前的具體狀態(tài):
Pending 待定:剛創(chuàng)建時(shí)的初始狀態(tài),還沒(méi)有確定執(zhí)行結(jié)果
Fulfilled 已兌現(xiàn):異步操作執(zhí)行成功,并返回一個(gè)值
Rejected 已拒絕:異步操作執(zhí)行失敗,并返回一個(gè)錯(cuò)誤原因
Settled 已敲定 / Resolved 已決議:“待定”狀態(tài)的反面,都表示異步操作已經(jīng)執(zhí)行完成,即已兌現(xiàn)或已拒絕
回調(diào)函數(shù)
如果完全不關(guān)心異步操作的執(zhí)行結(jié)果,那就把它放在那自己執(zhí)行就可以了;但通常情況下我們總是要對(duì)操作執(zhí)行的結(jié)果進(jìn)行后續(xù)處理的,例如更改頁(yè)面上的數(shù)據(jù)顯示、錯(cuò)誤處理等。但由于異步操作不知道什么時(shí)候可以執(zhí)行完成,就出現(xiàn)了“回調(diào)函數(shù)”的概念,意思就是等到異步操作處理結(jié)束了,再回過(guò)頭來(lái)調(diào)用這個(gè)函數(shù)來(lái)對(duì)執(zhí)行結(jié)果進(jìn)行處理。
傳統(tǒng)做法是,在執(zhí)行異步操作的時(shí)候就把回調(diào)函數(shù)作為參數(shù)傳進(jìn)去,比如最常見(jiàn)的:
setTimeout(function(){ console.log("成功!"); }, 250);
setTimeout()
函數(shù)是最常見(jiàn)的異步函數(shù)之一,眾所周知它的作用就是在指定時(shí)間后執(zhí)行指定代碼。仔細(xì)看就會(huì)發(fā)現(xiàn),setTimeout()
函數(shù)接收兩個(gè)參數(shù),第二個(gè)參數(shù)是等待時(shí)間,而第一個(gè)參數(shù)就是回調(diào)函數(shù),即等待指定的時(shí)間之后要回來(lái)調(diào)用這個(gè)函數(shù)。
很顯然這種傳參的做法有很多不方便的地方,比如把對(duì)結(jié)果的后續(xù)處理和異步操作本身耦合在了一起,以及著名的回調(diào)地獄:
doSomething(function(result) { doSomethingElse(result, function(newResult) { doThirdThing(newResult, function(finalResult) { console.log('Got the final result: ' + finalResult); }, failureCallback); }, failureCallback); }, failureCallback);
Promise.then() 綁定回調(diào)函數(shù)
有了 Promise 之后,就能把回調(diào)和異步操作本身分開(kāi)了。無(wú)論一個(gè) Promise 對(duì)象當(dāng)前是否已經(jīng)執(zhí)行完畢,我們都能在它上面綁定回調(diào)函數(shù),并且保證回調(diào)函數(shù)被執(zhí)行;這就是喜聞樂(lè)見(jiàn)的 then()
方法。
p.then(onFulfilled[, onRejected]); p.then(value => { // fulfillment }, reason => { // rejection });
then() 方法的語(yǔ)法很簡(jiǎn)單,有兩個(gè)可選參數(shù),分別代表當(dāng) Promise 的狀態(tài)變?yōu)槌晒Γ╢ulfilled)和失?。╮ejected)時(shí)所使用的回調(diào)函數(shù)。
如果只想綁定 onRejected
(即失敗時(shí)的錯(cuò)誤處理函數(shù)),下面兩種寫法完全等價(jià),第二種是第一種的簡(jiǎn)寫形式。
p.then(null, failureCallback); p.catch(failureCallback);
使用 Promise:鏈?zhǔn)秸{(diào)用
如果只是用 then 來(lái)綁定回調(diào)函數(shù),那并不能解決回調(diào)地獄的問(wèn)題。然而很妙的地方來(lái)了:Promise 支持鏈?zhǔn)秸{(diào)用:
doSomething().then(function(result) { return doSomethingElse(result); }) .then(function(newResult) { return doThirdThing(newResult); }) .then(function(finalResult) { console.log('Got the final result: ' + finalResult); }) .catch(failureCallback);
鏈?zhǔn)秸{(diào)用的實(shí)現(xiàn)
能做到鏈?zhǔn)秸{(diào)用的魔法來(lái)自 then()
方法:它會(huì)在執(zhí)行相應(yīng)的回調(diào)函數(shù)之后,返回一個(gè)新的 Promise 對(duì)象,并且插入 Promise 鏈的當(dāng)前位置。
這里稍微有點(diǎn)繞,容易把回調(diào)函數(shù)等同于 then() 方法本身。實(shí)際上成功/失敗的回調(diào)函數(shù)只是 then() 的參數(shù)而已;而實(shí)際執(zhí)行 then() 的時(shí)候,它會(huì)先根據(jù) promise 的狀態(tài)調(diào)用相應(yīng)的回調(diào)函數(shù),再根據(jù)回調(diào)函數(shù)的執(zhí)行結(jié)果生成一個(gè)新的 Promise 對(duì)象并返回;具體的對(duì)應(yīng)規(guī)則如下:
回調(diào)函數(shù)執(zhí)行情況 | then() 返回的 Promise 對(duì)象 |
---|---|
返回值 return x; | fulfilled 狀態(tài),參數(shù)為 x |
直接返回 return; / 無(wú) return 語(yǔ)句 | fulfilled 狀態(tài),參數(shù)為 undefined |
拋出錯(cuò)誤 throw err; | rejected 狀態(tài),參數(shù)為 err |
返回已決議的 Promise | 狀態(tài)和參數(shù)與返回的 Promise 一致 |
返回未定的 Promise | 未定的 Promise,回調(diào)參數(shù)與返回的相同 |
下面這個(gè)例子中,初始 Promise 的狀態(tài)為已拒絕,然后第一個(gè) then() 調(diào)用了綁定的 onRejected,返回了狀態(tài)為 fulfilled 的新 Promise 對(duì)象,并傳遞給了鏈中的下一個(gè) then():
Promise.reject() .then(() => 99, () => 42) // 調(diào)用 onRejected(return 42;),表格中的第一種情況 .then(solution => console.log('Resolved with ' + solution)); // Resolved with 42
同時(shí),你可能還記得 then() 的參數(shù)定義,兩個(gè)回調(diào)函數(shù)都是可選的;如果沒(méi)有傳入對(duì)應(yīng)的回調(diào)函數(shù),then() 會(huì)直接把原 promise 的終態(tài)返回,不做額外處理。
錯(cuò)誤處理
遇到異常拋出(被拒絕的 promise)時(shí),瀏覽器會(huì)順著 Promise 鏈尋找下一個(gè) onRejected
回調(diào)函數(shù)(經(jīng)常被簡(jiǎn)寫為 .catch()
),并跳過(guò)中間的 onFulfilled
回調(diào)函數(shù)。這種執(zhí)行邏輯與同步代碼中的 try-catch 執(zhí)行過(guò)程非常相似:
// 異步 Promise doSomething() .then(result => doSomethingElse(result)) .then(newResult => doThirdThing(newResult)) .then(finalResult => console.log(`Got the final result: ${finalResult}`)) .catch(failureCallback); // 同步 try { let result = syncDoSomething(); let newResult = syncDoSomethingElse(result); let finalResult = syncDoThirdThing(newResult); console.log(`Got the final result: ${finalResult}`); } catch(error) { failureCallback(error); }
一個(gè)具體的例子:
Promise.resolve() .then(() => { throw new Error('出錯(cuò)'); console.log('a'); }) .then(() => { console.log('b'); }) .catch(() => { console.log('c'); }) .then(() => { console.log('d'); }); // 輸出結(jié)果: // "c" // "d"
常見(jiàn)錯(cuò)誤
doSomething().then(function(result) { doSomethingElse(result) // 沒(méi)有返回 Promise 以及沒(méi)有必要的嵌套 Promise .then(newResult => doThirdThing(newResult)); }).then(() => doFourthThing()); // 最后,是沒(méi)有使用 catch 終止 Promise 調(diào)用鏈,可能導(dǎo)致沒(méi)有捕獲的異常
上面這個(gè)例子在 Promise 中進(jìn)行了嵌套,但沒(méi)有將嵌套的 Promise 對(duì)象返回,因此doFourthThing()
不會(huì)等待 doSomethingElse()
或 doThirdThing()
完成,而是并行運(yùn)行;并且如果有傳入?yún)?shù),接收到的會(huì)是 undefined
而不是 doThirdThing() 的執(zhí)行結(jié)果。
正確的寫法應(yīng)該是:
注:箭頭函數(shù) () => x
是 () => { return x; }
的簡(jiǎn)寫,即返回了新的 Promise 對(duì)象
doSomething() .then(function(result) { return doSomethingElse(result); }) .then(newResult => doThirdThing(newResult)) .then(() => doFourthThing()) .catch(error => console.log(error));
創(chuàng)建 Promise 對(duì)象
如果要執(zhí)行的異步操作沒(méi)有返回 Promise 對(duì)象,可以用 new 和構(gòu)造器創(chuàng)建自己的 promise。構(gòu)造器的兩個(gè)參數(shù)的作用是在異步操作成功/失敗時(shí),轉(zhuǎn)換 Promise 對(duì)象的狀態(tài)并傳遞對(duì)應(yīng)參數(shù)。
const myFirstPromise = new Promise((resolve, reject) => { // 做一些異步操作,最終會(huì)調(diào)用下面兩者之一: // resolve(someValue); // fulfilled // reject("failure reason"); // rejected }); // 一個(gè)例子 function myAsyncFunction(url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open("GET", url); xhr.onload = () => resolve(xhr.responseText); xhr.onerror = () => reject(xhr.statusText); xhr.send(); }); };
Promise 其他靜態(tài)方法
創(chuàng)建已決議的 Promise 對(duì)象
Promise.resolve(value)
和 Promise.reject(reason)
方法分別返回一個(gè)已經(jīng)成功/失敗的 Promise 對(duì)象。
const p1 = new Promise((resolve, reject) => { resolve(); }); const p2 = Promise.resolve();
如果 resolve(value)
的參數(shù)是帶有 then() 方法的 Promise 對(duì)象,函數(shù)將返回其自帶 then() 方法的執(zhí)行結(jié)果;如果參數(shù)為空或是基本類型,返回的Promise對(duì)象與在 then() 方法中 return 對(duì)應(yīng)值的結(jié)果一致,參見(jiàn)上文表格?;谶@樣的特性, resolve(value)
方法可以用于將不確定是否為 Promise 對(duì)象的 value 值統(tǒng)一為 Promise。
多個(gè) Promise 對(duì)象
Promise.all(iterable)
- 參數(shù)列表中的所有的 promises 都成功時(shí),返回一個(gè) fulfilled 的 Promise 對(duì)象,參數(shù)值是所有 promises 成功返回值的列表(順序不變)
- 如果任何一個(gè) promise 失敗,立即返回一個(gè) rejected 的 Promise 對(duì)象,參數(shù)是這個(gè)失敗 promise 的錯(cuò)誤信息
Promise.all([func1(), func2(), func3()]) .then(([result1, result2, result3]) => { /* use result1, result2 and result3 */ });
Promise.allSettled(iterable)
列表中所有 promises 都已敲定后返回一個(gè)promise,并帶有一個(gè)對(duì)象數(shù)組,對(duì)應(yīng)每個(gè)promise 的結(jié)果。
Promise.any(iterable)
當(dāng)列表中的任意一個(gè) promise 成功時(shí),立即返回這個(gè) promise 的值。
Promise.race(iterable)
當(dāng)列表中任意一個(gè) promise 成功或失敗時(shí),立即返回該 promise 對(duì)象的執(zhí)行結(jié)果。
一個(gè)綜合例子(使用 setTimeout 模擬異步操作):
// 創(chuàng)造一個(gè)狀態(tài)為 fulfilled,參數(shù)為"foo"的 Promise 對(duì)象 Promise.resolve("foo") .then(function(string) { // string: "foo" // 返回狀態(tài)為 fulfilled,參數(shù)為"foobar"的對(duì)象 return new Promise(function(resolve, reject) { setTimeout(function() { string += 'bar'; resolve(string); }, 1); }); }) .then(function(string) { // string: "foobar" setTimeout(function() { string += 'baz'; console.log(string); }, 1) // 返回值"foobar" return string; }) .then(function(string) { // string: "foobar" console.log("Last Then"); console.log(string); }); // 輸出結(jié)果: // Last Then // foobar // foobarbaz(由第二個(gè) then 中的 setTimeout 輸出)
結(jié)語(yǔ)&參考文獻(xiàn)
以上是閱讀學(xué)習(xí)了 MDN 文檔后個(gè)人總結(jié)的學(xué)習(xí)筆記,可能存在錯(cuò)誤和疏漏,歡迎指正與討論!
到此這篇關(guān)于JS Promise axios 請(qǐng)求結(jié)果后面的.then() 是什么意思 的文章就介紹到這了,更多相關(guān)JS Promise axios .then() 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳細(xì)聊聊TypeScript中unknown與any的區(qū)別
unknown類型比較謙虛,就和他本身的意思一樣,他從不禍害到其他的變量,但是any類型就是那種惡霸,屬于什么都不管,誰(shuí)也不敢管的類型,這篇文章主要給大家介紹了關(guān)于TypeScript中unknown與any區(qū)別的相關(guān)資料,需要的朋友可以參考下2021-10-10詳解webpack4.x之搭建前端開(kāi)發(fā)環(huán)境
這篇文章主要介紹了詳解webpack4.x之搭建前端開(kāi)發(fā)環(huán)境,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-03-03javascript event在FF和IE的兼容傳參心得(絕對(duì)好用)
event在IE和FF不兼,下面為大家分享的是javascript event在FF和IE的兼容傳參心得,需要的朋友可以參考下2014-07-07Javascript前端下載后臺(tái)傳來(lái)的文件流代碼實(shí)例
這篇文章主要介紹了Javascript前端下載后臺(tái)傳來(lái)的文件流代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08TypeError document.getElementById(...) is null錯(cuò)誤原因
這篇文章主要介紹了TypeError document.getElementById(...) is null錯(cuò)誤原因,這是很容易犯的一個(gè)低級(jí)錯(cuò)誤,需要的朋友可以參考下2015-05-05JavaScript ES6常用基礎(chǔ)知識(shí)總結(jié)
ES6中為我們提供了很多好用的新特性,其中包括let,箭頭函數(shù)以及擴(kuò)展運(yùn)算符…等,以下就是總結(jié)的常用基礎(chǔ)知識(shí)2019-02-02JavaScript數(shù)組排序小程序?qū)崿F(xiàn)解析
這篇文章主要介紹了JavaScript數(shù)組排序小程序?qū)崿F(xiàn)解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01原生js實(shí)現(xiàn)移動(dòng)端瀑布流式代碼示例
這篇文章主要為大家分享了原生js實(shí)現(xiàn)移動(dòng)端瀑布流式代碼示例,對(duì)瀑布流布局感興趣的小伙伴們可以參考一下2015-12-12