async 函数返回的 Promise 状态何时变为 resolved

21 阅读4分钟

今天我们来聊一个前端开发中非常基础,但又常常让人感到困惑的问题: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');

猜猜输出顺序是什么?

  1. Step 1
  2. Promise created: Promise {<pending>}
  3. End of script
  4. Promise 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');

输出顺序:

  1. Function start
  2. Script end (大约1秒后...)
  3. Timer done
  4. After await: Data from timer
  5. Promise 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
});

这里有一个非常重要的细节。你可能会认为流程是:

  1. async 函数执行完毕。
  2. 外部 Promise 被 resolve,其值是一个新的 Promise 对象。
  3. 外部 Promise 的 .then() 收到这个 Promise 对象。

但事实并非如此!JavaScript 会对 async 函数的返回值进行特殊处理

如果 async 函数的返回值是一个 Promise(我们称为“内部Promise”),那么外部 Promise 的状态将与这个内部 Promise “联动”。外部 Promise 会等待内部 Promise 敲定(settle),然后以相同的状态和值被敲定。

所以上面的例子中:

  • returnPromise 返回的外部 Promise 会等待 setTimeout 500ms。
  • 500ms后,内部 Promise 被 resolve('Inner Promise Value')
  • 紧接着,外部 Promise 也被 resolve('Inner Promise Value'),而不是一个 Promise 对象。 这个特性让 async 函数可以无缝地组合。

对比表格:清晰理解状态变化时机

场景async 函数返回的 Promise 状态变化时机resolve 值
普通返回 return value;函数体同步代码执行到 return 语句时value
无 return 语句函数体所有代码执行完毕时undefined
遇到 awaitawait 的 Promise 解决后,函数体继续执行,直到 return 或结束return 的值(或 undefined
内部抛出错误 throw error;错误被抛出的瞬间变为 rejected,值为 error
await 被拒绝的 Promise await Promise.reject(...)await 表达式得到拒绝结果的瞬间变为 rejected,值为拒绝原因
返回一个 Promise return somePromise;等待 somePromise 敲定后,立即以相同状态和值敲定somePromise 的解决值

希望这篇文章帮你理清了 async 函数与 Promise 状态变化之间的关系。下次当你使用 async/await 时,可以更自信地预判代码的执行流了。