如何在 async 函数中应对 Promise 的挂起状态

作者:袖梨 2026-07-14
async函数中Promise的pending状态不可直接检测,需通过加载状态管理、超时保护、取消机制和防重复触发来合理响应等待过程。

在 async 函数中,Promise 的“挂起状态”(pending)本身不可直接检测或干预——它只是 Promise 的初始中间态,async/await 机制会自动等待其 settled(fulfilled 或 rejected)。真正需要关注的,是**如何合理响应、控制或感知这种等待过程**,比如显示加载状态、设置超时、取消请求或避免重复触发。

显式管理加载状态

前端最常见需求是 UI 层反馈:用户点击后显示“加载中”,等 Promise 完成后再更新界面。async 函数内部不暴露 pending 状态,但你可以用变量配合 await 做状态同步:

  • 定义一个响应式状态(如 React 的 useState、Vue 的 ref 或普通变量)
  • 在 await 前设为 loading = true,await 后设为 false
  • 注意:若 Promise 永远 pending(如网络卡死),需配合超时机制,否则 loading 状态永不结束

为 Promise 添加超时保护

原生 Promise 没有内置超时,挂起太久会导致界面假死。可用 Promise.race 包装一个限时拒绝的 Promise:

function timeout(ms, promise) {  const controller = new AbortController();  const timeoutId = setTimeout(() => controller.abort(), ms);    return Promise.race([    promise,    new Promise((_, reject) =>       controller.signal.addEventListener('abort', () =>         reject(new Error('Request timed out'))      )    )  ]).finally(() => clearTimeout(timeoutId));}// 在 async 函数中使用async function fetchData() {  try {    const data = await timeout(5000, fetch('/api/data').then(r => r.json()));    return data;  } catch (err) {    console.error(err.message); // "Request timed out" 或其他错误  }}

支持可取消的异步操作

挂起中的 Promise 无法中途取消,但可通过 AbortSignal 让底层 API(如 fetch、setTimeout 封装)响应取消信号:

  • 创建 AbortController 实例,传入 signal 到 fetch 或自定义异步函数
  • 在 await 前保存 controller,需要时调用 controller.abort()
  • 被取消的 fetch 会立即 reject 并抛出 DOMException("AbortError")

示例:

let controller;async function startRequest() {  controller?.abort(); // 取消前一次  controller = new AbortController();    try {    const res = await fetch('/slow-api', { signal: controller.signal });    return await res.json();  } catch (err) {    if (err.name === 'AbortError') {      console.log('请求已被取消');    } else {      throw err;    }  }}

避免意外的 pending 积压

多次快速触发同一 async 函数(如搜索框连续输入),可能产生多个 pending Promise,造成资源浪费或状态错乱:

  • 使用防抖(debounce)延迟执行,只保留最后一次请求
  • Promise.allSettledPromise.all 协调多个并行请求的完成时机
  • 对已发起但未完成的请求,主动 abort(见上一节),而非放任其 pending

不复杂但容易忽略:async 函数的 pending 是隐式的,关键不在“观察它”,而在“设计好它的开始、中断与结束”。

相关文章

精彩推荐