前端中断请求的方式

103 阅读2分钟

Axios.CancelToken

axios对象有一个属性叫CancelToken,该属性提供了中断已经发出去的请求的方式。

执行器模式

<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
  const CancelTokenFunc = axios.CancelToken;
  let cancel;
​
  // 发送请求
  axios
    .get("https://jsonplaceholder.typicode.com/todos/1", {
      cancelToken: new CancelTokenFunc(function executor(c) {
        // 将 cancel 函数赋值给外部变量
        cancel = c;
      }),
    })
    .catch((error) => {
       console.log(error.message);
    });
​
  // 取消请求
  setTimeout(() => {
    cancel("Operation canceled by the user.");
  }, 1000);
</script>

令牌模式

// 创建一个 CancelToken 源
const CancelTokenFunc = axios.CancelToken;
const { token, cancel } = CancelTokenFunc.source();
​
// 发送请求
axios
  .get("https://jsonplaceholder.typicode.com/todos/1", {
    cancelToken: token,
  })
  .catch((error) => {
    console.log(error.message);
  });
​
// 取消请求
setTimeout(() => {
  cancel("Operation canceled by the user.");
}, 1000);

AbortController

AbortController是一个Web API,用于控制和管理可中止的异步操作,例如 fetch 请求、DOM 操作。

中止请求

<!DOCTYPE html>
<html>
  <head>
    <title>中断请求demo</title>
  </head>
  <body>
    <script>
      // 创建一个 AbortController 信号源
      const controller = new AbortController();
      const { signal } = controller;
​
      // 发送请求
      fetch("https://jsonplaceholder.typicode.com/todos/1", {
        signal,
      }).catch((error) => {
        console.log(error);
        if (error.name === 'AbortError') {
          console.log('Fetch 请求已被取消');
        } else {
          // 处理其他错误
        }
      });
​
      // 取消请求
      setTimeout(() => {
        controller.abort("Operation canceled by the user.");
      }, 1000);
    </script>
  </body>
</html>

终止axios请求

const controller = new AbortController();

axios.get('/foo/bar', {
   signal: controller.signal
}).then(function(response) {
   //...
});
// 取消当前请求
controller.abort()

AbortController和addEventListener配合使用

我们知道,取消监听器的方法removeEventListener(type, callback),这个callback必须和开启监听的callback是同一个函数引用,也就是说得把callback存下来,但有了signal就不用了

 const controller = new AbortController();
 function callback (e) {
   document.addEventListener('mousemove',  (e) => {
     // 回调处理函数
     // ...
   },
   { signal: controller.signal});
 }
 document.addEventListener('mousedown', callback);
 document.addEventListener('mouseup', controller.abort);

实现一个可以主动取消的 Promise

我们用 AbortController 以及 AbortSignal 可以很方便的构造出我们自定义的可以取消的 Promise 了。

/**
 * 自定义的可以主动取消的 Promise
 */

function myCoolPromise ({ signal }) {
  return new Promise((resolve, reject) => {
    // 如果刚开始 signal 存在并且是终止的状态可以直接抛出异常
    signal?.throwIfAborted();

    // 异步的操作,这里使用 setTimeout 模拟
    setTimeout(() => {
      Math.random() > 0.5 ? resolve('ok') : reject(new Error('not good'));
    }, 1000);

    // 添加 abort 事件监听,一旦 signal 状态改变就将 Promise 的状态改变为 rejected
    signal?.addEventListener('abort', () => reject(signal?.reason));
  });
}

/**
 * 使用自定义可取消的 Promise
 */

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

myCoolPromise({ signal }).then((res) => console.log(res), err => console.warn(err));
setTimeout(() => {
  ac.abort();
}, 100); // 可以更改时间看不同的结果