JS Promise axios 請求結果後面的.then() 是什麼意思
目錄
- Promise 物件
- Promise 物件的狀態
- 回撥函式
- Promise.then() 繫結回撥函式
- 使用 Promise:鏈式呼叫
- 鏈式呼叫的實現
- 錯誤處理
- 常見錯誤
- 建立 Promise 物件
- Promise 其他靜態方法
- 建立已決議的 Promise 物件
- 多個 Promise 物件
- 結語&參考文獻
Promise 是中一種處理非同步操作的機制,在現在的前端程式碼中使用頻率很高。Promise 這個詞可能有點眼生,但你肯定見過axios.get(...).then(res => {...})
;用於非同步請求的 axios 返回的就是一個 Promise 物件。
平時一直在程式碼中.then()
.catch()
地寫來寫去,終於決定要認真學一學這個 Promise 到底是怎麼回事,希望這篇學習筆記也能幫到你。
Promise 物件
一個 Promise 物件表示一個非同步操作的執行結果,包括狀態(成功/失敗)和值(成功返回值/錯誤原因)。一個 Promise 在建立的時候非同步操作可能還沒執行完成,通過持有這個 Promise 物件,可以在未來非同步操作完成的時候對結果進行相應操作。
Promise 物件的狀態
這裡有幾個常用的名詞,用來表示 Promise 物件當前的具體狀態:
Pending 待定:剛建立時的初始狀態,還沒有確定執行結果
Fulfilled 已兌現:非同步操作執行成功,並返回一個值
Rejected 已拒絕:非同步操作執行失敗,並返回一個錯誤原因
Settled 已敲定 / Resolved 已決議:“待定”狀態的反面,都表示非同步操作已經執行完成,即已兌現或已拒絕
回撥函式
如果完全不關心非同步操作的執行結果,那就把它放在那自己執行就可以了;但通常情況下我們總是要對操作執行的結果進行後續處EwpwfA理的,例如更改頁面上的資料顯示、錯誤處理等。但由於非同步操作不知道什麼時候可以執行完成,就出現了“回撥函式”的概念,意思就是等到非同步操作處理結束了,再回過頭來呼叫這個函式來對執行結果進行處理。
傳統做法是,在執行非同步操作的時候就把回撥函式作為引數傳進去,比如最常見的:
setTimeout(function(){ console.log("成功!"); },250);
setTimeout()
函式是最常見的非同步函式之一,眾所周知它的作用就是在指定時間後執行指定程式碼。仔細看就會發現,setTimeout()
函式接收兩個引數,第二個引數是等待時間,而第一個引數就是回撥函式,即等待指定的時間之後要回來呼叫這個函式。
很顯然這種傳參的做法有很多不方便的地方,比如把對結果的後續處理和非同步操作本身耦合在了一起,以及著名的回撥地獄:
doSomething(function(result) { doSomethingElse(result,function(newResult) { doThirdThing(newResult,function(finalResult) { console.log('Got the final result: ' + finalResult); },failureCallback); },failureCallback); },failureCallback);
Promise.then() 繫結回撥函式
有了 Promise 之後,就能把回撥和非同步操作本身分開了。無論一個 Promise 物件當前是否已經執行完畢,我們都能在它上面繫結回撥函式,並且保證回撥函式被執行;這就是喜聞樂見的then()
方法。
p.then(onFulfilled[,onRejected]); p.then(value => { // fulfillment },reason => { // rejection });
then() 方法的語法很簡單,有兩個可選引數,分別代表當 Promise 的狀態變為成功(fulfilled)和失敗(rejected)時所使用的回撥函式。
如果只想繫結onRejected
(即失敗時的錯誤處理函式),下面兩種寫法完全等價,第二種是第一種的簡寫形式。
p.then(null,failureCallback); p.catch(failureCallback);
使用 Promise:鏈式呼叫
如果只是用 then 來繫結回撥函式,那並不能解決回撥地獄的問題。然而很妙的地方來了:Promise 支援鏈式呼叫:
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);
鏈式呼叫的實現
能做到鏈式呼叫的魔法來自then()
方法:它會在執行相應的回撥函式之後,返回一個新的 Promise 物件,並且插入 Promise 鏈的當前位置。
這裡稍微有點繞,容易把回撥函式等同於 then() 方法本身。實際上成功/失敗的回撥函式只是 then() 的引數而已;而實際執行 then() 的時候,它會先根據 promise 的狀態呼叫相應的回撥函式,再根據回撥函式的執行結果生成一個新的 Promise 物件並返回;具體的對應規則如下:
回撥函式執行情況 | then() 返回的 Promise 物件 |
---|---|
返回值return x; | fulfilled 狀態,引數為 x |
直接返回return; / 無 return 語句 | fulfilled 狀態,引數為 undefined |
丟擲錯誤throw err; | rejected 狀態,引數為 err |
返回已決議的 Promise | 狀態和引數與返回的 Promise 一致 |
返回未定的 Promise | 未定的 Promise,回撥引數與返回的相同 |
下面這個例子中,初始 Promise 的狀態為已拒絕,然後第一個 then() 呼叫了繫結的 onRejected,返回了狀態為 fulfilled 的新 Promise 物件,並傳遞給了鏈中的下一個 then():
Promise.reject() .then(() => 99,() => 42) // 呼叫 onRejected(return 42;),表格中的第一種情況 .then(solution => console.log('Resolved with ' + solution)); // Resolved with 42
同時,你可能還記得 then() 的引數定義,兩個回撥函式都是可選的;如果沒有傳入對應的回撥函式,then() 會直接把原 promise 的終態返回,不做額外處理。
錯誤處理
遇到異常丟擲(被拒絕的 promise)時,瀏覽器會順著 Promise 鏈尋找下一個onRejected
回撥函式(經常被簡寫為.catch()
),並跳過中間的onFulfilled
回撥函式。這種執行邏輯與同步程式碼中的 try-catch 執行過程非常相似:
// 非同步 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); }
一個具體的例子:
Promise.resolve() .then(() => { throw new Error('出錯'); console.log('a'); }) .then(() => { console.log('b'); }) .catch(() => { console.log('c'); }) .then(() => { console.log('d'); }); // 輸出結果: // "c" // "d"
常見錯誤
doSomething().then(function(result) {
doSomethingElse(result) // 沒有返回 Promise 以及沒有必要的巢狀 Promise
.then(newResult => doThirdThing(newResult));
}).then(() => doFourthThing());
// 最後,是沒有使用 catch 終止 Promise 呼叫鏈,可能www.cppcns.com導致沒有捕獲的異常
上面這個例子在 Promise 中進行了巢狀,但沒有將巢狀的 Promise 物件返回,因此doFourthThing()
不會等待doSomethingElse()
或doThirdThing()
完成,而是並行執行;並且如果有傳入引數,接收到的會是undefined
而不是 doThirdThing() 的執行結果。
正確的寫法應該是:
注:箭頭函式() => x
是() => { return x; }
的簡寫,即返回了新的 Promise 物件
doSomething() .then(function(result) { return doSomethingElse(result); }) .then(newResult => doThirdThing(newResult)) .then(() => doFourthThing()) .catch(error => console.log(error));
建立 Promise 物件
如果要執行的非同步操作沒有返回 Promise 物件,可以用 new 和構造器建立自己的 promise。構造器的兩個引數的作用是在非同步操作成功/失敗時,轉換 Promise 物件的狀態並傳遞對應引數。
const myFirstPromise = new Promise((resolve,reject) => { // 做一些非同步操作,最終會呼叫下面兩者之一: // resolve(someValue); // fulfilled // reject("failure reason"); // rejected }); // 一個例子 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 其他靜態方法
建立已決議的 Promise 物件
Promise.resolve(value)客棧
和Promise.reject(reason)
方法分別返回一個已經成功/失敗的 Promise 物件。
const p1 = new Promise((resolve,reject) => { resolve(); }); const p2 = Promise.resolve();
如果resolve(value)
的引數是帶有 then() 方法的 Promise 物件,函式將返回其自帶 then() 方法的執行結果;如果引數為空或是基本型別,返回的Promise物件與在 then() 方法中 return 對應值的結果一致,參見上文表格。基於這樣的特性,resolve(value)
方法可以用於將不確定是否為 Promise 物件的 value 值統一為 Promise。
多個 Promise 物件
Promise.all(iterable)
- 引數列表中的所有的 promises 都成功時,返回一個 fulfilled 的 Promise 物件,引數值是所有 promises 成功返回值的列表(順序不變)
- 如果任何一個 promise 失敗,立即返回一個 rejected 的 Promise 物件,引數是這個失敗 promise 的錯誤資訊
Promise.all([func1(),func2(),func3()]) .then(([result1,result2,result3]) => { /* use result1,result2 and result3 */ });
Promise.allSettled(iterable)
列表中所有 promises 都已敲定後返回一個promise,並帶有一個物件陣列,對應每個promise 的結果。
Promise.any(iterable)
當列表中的任意一個 promise 成功時,立即返回這個 promise 的值。
Promise.race(iterable)
當列表中任意一個 promise 成功或失敗時,立即返回該 promise 物件的執行結果。
一個綜合例子(使用 setTimeout 模擬非同步操作):
// 創造一個狀態為 fulfilled,引數為"foo"的 Promise 物件 Promise.resolve("foo") .then(function(string) { // string: "foo" // 返回狀態為 fulfilled,引數為"foobar"的物件 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); }); // 輸出結果: // Last Then // foobar // foobarbaz(由第二個 then 中的 setTimeout 輸出)
結語&參考文獻
以上是閱讀學習了 MDN 文件後個人總結的學習筆記,可能存在錯誤和疏漏,歡迎指正與討論!
MDN-Promise
MDN-使用Promise
MDN-Promise.then()
到此這篇關於JS Promise axios 請求結果後面的.then() 是什麼意思 的文章就介紹到這了,更多相關JS Promise axios .then() 內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!