在平时项目开发中,经常遇到这些问题:

  • 用户快速切换页面:旧请求还没返回,新页面的请求已经发出,导致页面闪烁或数据错乱。
  • 搜索框实时输入:用户频繁输入关键词,旧请求结果覆盖了最新结果。
  • 组件卸载时仍在更新状态:报错 Cannot update state on an unmounted component
  • 长耗时请求:用户想手动取消,或需要设置超时时间,避免页面卡死。
  • 批量请求管理困难:进入一个页面时要请求多个接口,但离开页面时要统一取消。

这些场景如果不处理好,不仅影响性能,还会带来糟糕的用户体验。
幸运的是,AbortController 提供了优雅的解决方案。


一、基础概念

  • AbortController:通过 new AbortController() 创建控制器。
  • signal:使用 controller.signal 传递给需要中止的请求。
  • abort():调用后,所有绑定该信号的请求都会立即中止。

二、实用特性与 Vue 项目示例

1. 中止单个请求(搜索输入场景)

👉 应用场景:防止旧搜索结果覆盖新结果。

<script setup>
import { ref } from "vue";

let controller = null;
const results = ref([]);

async function search(keyword) {
  // 中止上一个请求
  if (controller) controller.abort();

  controller = new AbortController();
  const { signal } = controller;

  try {
    const res = await fetch(`/api/search?q=${keyword}`, { signal });
    results.value = await res.json();
  } catch (err) {
    if (err.name === "AbortError") {
      console.log("请求被中止:", keyword);
    }
  }
}
</script>

<template>
  <div>
    <input type="text" @input="e => search(e.target.value)" placeholder="搜索..." />
    <ul>
      <li v-for="item in results" :key="item.id">{{ item.name }}</li>
    </ul>
  </div>
</template>

2. 一个信号控制多个请求(页面卸载时统一中止)

👉 应用场景:进入新页面时,老页面的所有请求都自动中止。

<script setup>
import { onBeforeUnmount, ref } from "vue";

const user = ref(null);
const messages = ref([]);
const notifications = ref([]);
const controller = new AbortController();
const { signal } = controller;

async function loadData() {
  try {
    const [u, m, n] = await Promise.all([
      fetch("/api/user", { signal }),
      fetch("/api/messages", { signal }),
      fetch("/api/notifications", { signal })
    ]);
    user.value = await u.json();
    messages.value = await m.json();
    notifications.value = await n.json();
  } catch (err) {
    if (err.name === "AbortError") {
      console.log("所有请求被中止");
    }
  }
}

loadData();

// 页面卸载时统一中止
onBeforeUnmount(() => controller.abort());
</script>

<template>
  <div>
    <h2>{{ user?.name }}</h2>
    <p>消息数量: {{ messages.length }}</p>
    <p>通知数量: {{ notifications.length }}</p>
  </div>
</template>

3. 组件卸载时中止请求(避免内存泄漏)

👉 应用场景:Vue 组件卸载时防止“状态更新到已销毁的组件”报错。

<script setup>
import { onBeforeUnmount, ref } from "vue";

const user = ref(null);
const controller = new AbortController();

fetch("/api/user", { signal: controller.signal })
  .then(res => res.json())
  .then(data => (user.value = data))
  .catch(err => {
    if (err.name === "AbortError") {
      console.log("请求被中止:组件卸载");
    }
  });

// 卸载时中止
onBeforeUnmount(() => controller.abort());
</script>

<template>
  <div>
    <h2 v-if="user">{{ user.name }}</h2>
    <p v-else>加载中...</p>
  </div>
</template>

4. 超时自动取消请求

👉 应用场景:请求超时自动取消,避免长时间无响应。

<script setup>
async function fetchWithTimeout(url, timeout = 5000) {
  const controller = new AbortController();
  const { signal } = controller;

  const timer = setTimeout(() => controller.abort(), timeout);

  try {
    const res = await fetch(url, { signal });
    return await res.json();
  } finally {
    clearTimeout(timer);
  }
}

fetchWithTimeout("/api/slow", 3000)
  .then(data => console.log("成功:", data))
  .catch(err => {
    if (err.name === "AbortError") {
      console.log("请求超时,已中止");
    }
  });
</script>

5. 封装请求工具,便于全局使用

// utils/http.js
export class HttpClient {
  constructor() {
    this.controller = null;
  }

  request(url, options = {}) {
    if (this.controller) this.controller.abort();
    this.controller = new AbortController();

    return fetch(url, { ...options, signal: this.controller.signal });
  }

  cancel() {
    if (this.controller) this.controller.abort();
  }
}
/// Vue中
<script setup>
import { HttpClient } from "@/utils/http";

const client = new HttpClient();

async function loadData() {
  try {
    const res = await client.request("/api/data");
    console.log(await res.json());
  } catch (err) {
    if (err.name === "AbortError") {
      console.log("请求被取消");
    }
  }
}

// 手动取消
setTimeout(() => client.cancel(), 1000);
</script>

三、总结

在项目中,AbortController 主要能解决以下问题:

  1. 搜索输入时中止旧请求 → 保证数据正确。
  2. 统一中止多个请求 → 页面切换时清理资源。
  3. 组件卸载时中止请求 → 避免内存泄漏与报错。
  4. 超时自动取消 → 提升用户体验。
  5. 工具类封装 → 增强代码复用性和可维护性。

一句话记忆:
AbortController 就是请求的“刹车器”,能在项目中随时中止请求,保证性能与体验。

更多推荐