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

今天我们来聊一个前端开发中非常基础,但又常常让人感到困惑的问题:async function 返回的 Promise,到底在什么时候会变成 resolved 状态?

核心规则:一句话概括

我们先说结论。记住这句话:

一个 async 函数返回的 Promise,会在函数体内部所有代码执行完毕后,变为 resolved 状态。它的 resolve 值,就是函数 return 语句的值。

听起来很简单,对吧?但魔鬼藏在细节里。我们通过几个例子,一层层剥开来看。

没有 await 的 async 函数

我们从最简单的开始。

arduino 复制代码
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 状态变化的时机。

javascript 复制代码
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

javascript 复制代码
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,情况就不同了。

javascript 复制代码
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 对象呢?

javascript 复制代码
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
遇到 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 时,可以更自信地预判代码的执行流了。

相关推荐
李剑一2 小时前
大屏天气展示太普通?视觉升级!用 Canvas 做动态天气遮罩,雷阵雨效果直接封神
前端·vue.js·canvas
Lee川2 小时前
现代Web开发中的CSS继承、Flexbox布局与LocalStorage交互:从文档解析到实践应用
前端·css
炫饭第一名2 小时前
速通Canvas指北🦮——图形、文本与样式篇
前端·javascript·程序员
本末倒置1832 小时前
面向 Vue 开发者的 Next.js 快速入门指南
前端·vue.js
暴走的小呆2 小时前
vue3暗影代理:非原始值的响应式迷局
前端
1024小神2 小时前
bun+hono实现websocket长链接通许的demo
前端
滕青山2 小时前
文本字符数统计 在线工具核心JS实现
前端·javascript·vue.js
十二7402 小时前
前端缓存踩坑实录:从版本号管理到自动化构建
前端·javascript·nginx
over6972 小时前
从 URL 输入到页面展示:一次完整的 Web 导航之旅
前端·面试·架构