async 函數(shù)的返回值到底是什么
async/await 已經(jīng)成為處理異步操作的標(biāo)配,它讓我們能夠用看似同步的方式書寫異步代碼,極大地提高了代碼的可讀性和可維護(hù)性。我們每天都在寫 await fetch(...)。
但:一個(gè) async 函數(shù),它的返回值究竟是什么?
場(chǎng)景一:返回一個(gè)非 Promise 值
這是最常見也最容易產(chǎn)生困惑的情況。我們來(lái)看一個(gè)最簡(jiǎn)單的 async 函數(shù):
async function getNumber() {
return 42; // 返回一個(gè)普通的數(shù)字
}
const result = getNumber();
console.log(result);
如果你運(yùn)行這段代碼,控制臺(tái)輸出的并不會(huì)是 42,而是:
Promise { <pending> }
很快,這個(gè) Promise 的狀態(tài)會(huì)變?yōu)?nbsp;fulfilled,并且其值為 42。
背后發(fā)生了什么?
當(dāng) async 函數(shù)的 return 語(yǔ)句返回一個(gè)非 Promise 值(如數(shù)字、字符串、對(duì)象等)時(shí),JavaScript 引擎會(huì)自動(dòng)將其包裝在一個(gè) resolved狀態(tài)的 Promise 中。換句話說(shuō),上面的代碼在底層等價(jià)于:
function getNumber() {
return Promise.resolve(42);
}
這就是為什么直接調(diào)用 getNumber() 會(huì)得到一個(gè) Promise。為了獲取到內(nèi)部的值 42,我們必須使用 await 或者 .then() 來(lái)“解包”:
場(chǎng)景二:返回一個(gè) Promise
如果 async 函數(shù)本身就返回一個(gè) Promise,情況會(huì)怎樣?JavaScript 引擎會(huì)再把它包一層,變成 Promise<Promise<T>> 嗎?
答案是:不會(huì)。
async 函數(shù)足夠智能,如果它檢測(cè)到返回值已經(jīng)是一個(gè) Promise,它會(huì)直接返回這個(gè) Promise,而不會(huì)進(jìn)行額外的包裝。
async function fetchUser() {
// 返回一個(gè)顯式的 Promise
return new Promise(resolve => {
setTimeout(() => {
resolve({ name: 'Alice' });
}, 1000);
});
}
const promise = fetchUser();
console.log(promise); // Promise { <pending> }
promise.then(user => {
console.log(user); // 1秒后輸出: { name: 'Alice' }
});
這個(gè)行為至關(guān)重要,它保證了 async 函數(shù)的返回值總是一個(gè)行為一致的、可 await 的對(duì)象,避免了不必要的 Promise 嵌套。
場(chǎng)景三:函數(shù)內(nèi)部拋出錯(cuò)誤
如果在 async 函數(shù)內(nèi)部 throw 一個(gè)錯(cuò)誤,會(huì)發(fā)生什么?程序會(huì)崩潰嗎?
不一定。async 函數(shù)會(huì)將拋出的錯(cuò)誤捕獲,并將其作為 一個(gè) rejected 狀態(tài)的 Promise 返回。
這個(gè) rejected Promise 的 reason 就是我們拋出的那個(gè) Error 對(duì)象。因此,我們可以用標(biāo)準(zhǔn)的 Promise 錯(cuò)誤處理方式來(lái)捕獲它:
// 使用 try...catch 配合 await
async function handleFailure() {
try {
await willFail();
} catch (error) {
console.error(error.message); // 輸出: Something went wrong!
}
}
handleFailure();
// 或者使用 .catch()
willFail().catch(error => {
console.error(error.message); // 輸出: Something went wrong!
});
這種機(jī)制將同步代碼中的 try...catch 錯(cuò)誤處理模型,無(wú)縫地融入到了異步流程控制中。
場(chǎng)景四:沒(méi)有 return 語(yǔ)句
如果一個(gè) async 函數(shù)執(zhí)行完畢但沒(méi)有 return 語(yǔ)句,它的返回值是什么?
和普通函數(shù)一樣,沒(méi)有 return 語(yǔ)句的函數(shù)會(huì)隱式地返回 undefined。根據(jù)場(chǎng)景一的規(guī)則,這個(gè) undefined 會(huì)被 async 關(guān)鍵字包裝成一個(gè) resolved 狀態(tài)的 Promise。
async function doNothing() {
const a = 1 + 1;
// 沒(méi)有 return
}
doNothing().then(value => {
console.log(value); // 輸出: undefined
});
所以,即使函數(shù)什么都不返回,它依然遵循“永遠(yuǎn)返回一個(gè) Promise”的黃金法則,只不過(guò)這個(gè) Promise 的 resolved 值是 undefined。
async/await 本質(zhì)上是 Promise 的語(yǔ)法糖。它的設(shè)計(jì)初衷就是為了讓開發(fā)者能夠以更直觀的方式處理異步邏輯。
async 的“包裝”行為和 await 的“解包”行為,兩者相輔相成,構(gòu)成了這套優(yōu)雅語(yǔ)法糖的核心。