今天我们来聊一个前端开发中非常基础,但又常常让人感到困惑的问题:async function 返回的 Promise,到底在什么时候会变成 resolved 状态?
核心规则:一句话概括
我们先说结论。记住这句话:
一个 async 函数返回的 Promise,会在函数体内部所有代码执行完毕后,变为
resolved状态。它的resolve值,就是函数return语句的值。
听起来很简单,对吧?但魔鬼藏在细节里。我们通过几个例子,一层层剥开来看。
没有 await 的 async 函数
我们从最简单的开始。
async function basicFunc() {
console.log('Step 1');
return 'Hello, World!';
}
const promise = basicFunc();
console.log('Promise created:', promise);
promise.then(value => {
console.log('Promise resolved with:', value);
});
console.log('End of script');
猜猜输出顺序是什么?
Step 1Promise created: Promise {<pending>}End of scriptPromise resolved with: Hello, World!
关键点:
- •
basicFunc被调用时,同步执行函数体内的console.log('Step 1')。 - • 然后,遇到
return 'Hello, World!'。此时,async 函数会立即返回一个 Promise,并且这个 Promise 的状态会迅速变为resolved,其值就是'Hello, World!'。 - • 但是,Promise 的
.then()回调属于微任务,它会被排入微任务队列,等待当前同步代码(即console.log('End of script'))全部执行完毕后,才会被执行。
所以,即使没有 await,async 函数返回的 Promise 也会在函数体同步代码执行完毕的瞬间变为 resolved。
核心场景:遇到 await 时会发生什么?
await 是 async 函数的灵魂。它的行为直接决定了 Promise 状态变化的时机。
async function funcWithAwait() {
console.log('Function start');
const result = await new Promise(resolve => {
setTimeout(() => {
console.log('Timer done');
resolve('Data from timer');
}, 1000);
});
console.log('After await:', result);
return 'Final Result';
}
const promise = funcWithAwait();
promise.then(value => console.log('Promise resolved:', value));
console.log('Script end');
输出顺序:
Function startScript end(大约1秒后...)Timer doneAfter await: Data from timerPromise resolved: Final Result
核心结论:
await会暂停 async 函数的执行,但不会暂停外部 Promise 的状态变化。外部 Promise 会一直保持pending,直到函数体真正执行到最后(遇到return或函数结尾),它才会被resolve。
几种特殊情况
1. 没有 return 语句
async 函数可以没有 return。
async function noReturn() {
console.log('Just do something');
await Promise.resolve();
// 没有 return 语句
}
noReturn().then(value => {
console.log('Resolved with:', value); // 输出:Resolved with: undefined
});
规则:如果一个 async 函数没有 return 语句,那么它返回的 Promise 在函数执行完毕后,会以 undefined 作为值被 resolve。
2. 抛出错误(Rejection)
如果 async 函数内部抛出错误,或者 await 了一个被 reject 的 Promise,情况就不同了。
async function throwError() {
console.log('Start');
throw new Error('Something went wrong!');
// 或者:await Promise.reject(new Error('...'));
}
throwError()
.then(value => console.log('Success:', value)) // 这行不会执行
.catch(error => console.error('Failed:', error.message)); // 输出:Failed: Something went wrong!
规则:只要 async 函数体内部(包括 await 表达式)发生了未被捕获的异常,它返回的 Promise 就会立即变为 rejected 状态。 函数后续的代码不会被执行。
3. 返回一个 Promise
如果 async 函数 return 了一个 Promise 对象呢?
async function returnPromise() {
console.log('Inside async function');
return new Promise(resolve => {
setTimeout(() => resolve('Inner Promise Value'), 500);
});
}
returnPromise().then(value => {
console.log('Outer promise resolved with:', value); // 输出:Outer promise resolved with: Inner Promise Value
});
这里有一个非常重要的细节。你可能会认为流程是:
- async 函数执行完毕。
- 外部 Promise 被
resolve,其值是一个新的 Promise 对象。 - 外部 Promise 的
.then()收到这个 Promise 对象。
但事实并非如此!JavaScript 会对 async 函数的返回值进行特殊处理:
如果 async 函数的返回值是一个 Promise(我们称为“内部Promise”),那么外部 Promise 的状态将与这个内部 Promise “联动”。外部 Promise 会等待内部 Promise 敲定(settle),然后以相同的状态和值被敲定。
所以上面的例子中:
returnPromise返回的外部 Promise 会等待setTimeout500ms。- 500ms后,内部 Promise 被
resolve('Inner Promise Value')。 - 紧接着,外部 Promise 也被
resolve('Inner Promise Value'),而不是一个 Promise 对象。 这个特性让 async 函数可以无缝地组合。
对比表格:清晰理解状态变化时机
| 场景 | async 函数返回的 Promise 状态变化时机 | resolve 值 |
|---|---|---|
普通返回 return value; | 函数体同步代码执行到 return 语句时 | value |
无 return 语句 | 函数体所有代码执行完毕时 | undefined |
遇到 await | await 的 Promise 解决后,函数体继续执行,直到 return 或结束 | return 的值(或 undefined) |
内部抛出错误 throw error; | 错误被抛出的瞬间 | 变为 rejected,值为 error |
await 被拒绝的 Promise await Promise.reject(...) | await 表达式得到拒绝结果的瞬间 | 变为 rejected,值为拒绝原因 |
返回一个 Promise return somePromise; | 等待 somePromise 敲定后,立即以相同状态和值敲定 | somePromise 的解决值 |
希望这篇文章帮你理清了 async 函数与 Promise 状态变化之间的关系。下次当你使用 async/await 时,可以更自信地预判代码的执行流了。