【AbortController】中断Promise请求,解决竞态条件问题

55 阅读2分钟

在项目开发中,处理异步操作是一个不可避免的任务。异步操作,尤其是网络请求,可能会导致竞态条件问题。这种情况通常发生在用户快速切换页面或进行其他操作时,导致多个请求同时进行,而不需要的请求结果可能会覆盖掉最新的状态。幸运的是,浏览器提供了 AbortController,一个强大的工具,可以帮助我们管理和取消不必要的异步请求,从而解决这些竞态条件问题。

什么是 AbortController?

AbortController 是一个内置于浏览器的 API,它允许你创建一个可以用于取消一个或多个 Fetch 请求的信号。通过使用 AbortController,你可以在需要时主动取消异步操作,避免处理过时的数据。

基本用法

首先,让我们看看如何使用 AbortController 来控制请求。

const controller = new AbortController();
const signal = controller.signal;

fetch('https://xxx/posts', { signal })
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => {
    if (error.name === 'AbortError') {
      console.log('Request was aborted');
    } else {
      console.error('Fetch error:', error);
    }
  });

// 取消请求
controller.abort();

在这个例子中,我们创建了一个 AbortController 实例,并从中获取了一个 signal 对象。这个 signal 被传递给 Fetch 请求。当我们调用 controller.abort() 时,请求会被取消,并且 Fetch 的 Promise 会被拒绝,同时抛出一个 AbortError

应用场景:避免竞态条件

考虑一个常见的场景:当用户改变内容:如下图勾选/取消勾选图层目录树更新图例信息,每次图层变化时都会触发一个新的网络请求来获取相关数据。如果用户快速更改多次,那么可能会有多个请求同时进行,可能导致竞态条件问题。我们可以使用 AbortController 来确保只有最后一个请求的结果被处理。

​编辑

/** 控制器-解决“竞态条件”问题 */
let currentAbortController: AbortController | null = null

/** 业务逻辑 */
const getXXXData = async () => {
  // 如果已经有一个请求在进行中,则取消它
  currentAbortController && currentAbortController.abort()
  // 创建新的控制器
  currentAbortController = new AbortController()

  const params = {
    xxx: 'xxx'
  }
  getXXXList(params, currentAbortController.signal)
    .then(res => {
      // ... 成功操作 xxx
    })
    .finally(() => {
      currentAbortController = null
    })
}

在这个示例中,每次用户变更数据时,都会取消之前的请求。这确保了只有当前查询的请求会被处理,从而避免了竞态条件。

结论

AbortController 为我们提供了一种优雅的方式来管理和取消不必要的异步请求,尤其是在处理竞态条件问题时。通过合理地使用 AbortController,我们可以提高应用的性能和响应能力,确保用户始终看到的是最新有效的数据。

至此已完成利用AbortController 来解决前端开发中的竞态条件问题 ^-^