js技巧--使用AbortController控制代码的异步行为

介绍

AbortController 接口表示一个控制器对象,允许你根据需要中止一个或多个 Web 请求。

你可以使用 AbortController() 构造函数创建一个新的 AbortController 对象。使用 AbortSignal 对象可以完成与异步操作的通信。

构造函数

AbortController()

创建一个新的 AbortController 对象实例。

实例属性

AbortController.signal 只读

返回一个 AbortSignal 对象实例,可以用它来和异步操作进行通信或者中止这个操作。

实例方法

AbortController.abort()

中止一个尚未完成的异步操作。这能够中止 fetch 请求及任何响应体和流的使用。

应用

取消网络发送

let controller;
let progressAnim;
let animCount = 0;

downloadBtn.addEventListener("click", fetchVideo);

abortBtn.addEventListener("click", () => {
  controller.abort();
  console.log("Download aborted");
  downloadBtn.classList.remove("hidden");
});

function fetchVideo() {
  controller = new AbortController();
  const signal = controller.signal;
  downloadBtn.classList.add("hidden");
  abortBtn.classList.remove("hidden");
  reports.textContent = "Video awaiting download...";
  fetch(url, { signal })
    .then((response) => {
      if (response.status === 200) {
        runAnimation();
        setTimeout(
          () => console.log("Body used: ", response.bodyUsed),
          1
        );
        return response.blob();
      } else {
        throw new Error("Failed to fetch");
      }
    })
    .then((myBlob) => {
      const video = document.createElement("video");
      video.setAttribute("controls", "");
      video.src = URL.createObjectURL(myBlob);
      videoWrapper.appendChild(video);

      videoWrapper.classList.remove("hidden");
      abortBtn.classList.add("hidden");
      downloadBtn.classList.add("hidden");

      reports.textContent = "Video ready to play";
    })
    .catch((e) => {
      abortBtn.classList.add("hidden");
      downloadBtn.classList.remove("hidden");
      reports.textContent = "Download error: " + e.message;
    })
    .finally(() => {
      clearInterval(progressAnim);
      animCount = 0;
    });
}

原生的fetch api是没办法取消请求的,我们需要取消fetch请求,就要用到AbortController。我们先使用 AbortController() 构造函数创建一个控制器,然后使用 AbortController.signal 属性获取其关联 AbortSignal 对象的引用。当 fetch 请求初始化时,我们将 AbortSignal 作为一个选项传递进入请求的选项对象中(下面的 {signal})。这将 signal 和 controller 与 fetch 请求相关联,并且允许我们通过调用 AbortController.abort() 去中止它。

在线运行这个case

https://codesandbox.io/p/sandbox/gcwt72?file=%2Findex.html

一个可中止的 WebSocket 连接

/**
 * 创建一个可中止的 WebSocket 连接
 * @param {string} url - WebSocket 服务器的 URL
 * @param {AbortSignal} signal - 用于控制 WebSocket 生命周期的 AbortSignal
 * @returns {WebSocket} 返回创建的 WebSocket 实例
 */
function abortableSocket(url, signal) {
  const w = new WebSocket(url);

  // 检查信号是否已中止,如果是则立即关闭 WebSocket
  if (signal.aborted) {
    w.close();
    return w;
  }

  // 监听 abort 事件,触发时关闭 WebSocket
  signal.addEventListener('abort', () => {
    // 使用标准关闭代码 1000 表示正常关闭
    w.close(1000, 'Connection aborted by AbortController');
  });

  return w;
}

创建一个可被外部信号控制的 WebSocket 连接。它接受一个 AbortSignal作为参数,允许在需要时(例如组件卸载、用户操作、超时等)通过调用对应的 controller.abort()方法来中止 WebSocket 连接。

巧妙的移除事件监听

export default class DataGrid {
    constructor(container, options) {
        this.container = container;
        this.options = options;
        this.controller = new AbortController(); 
        this.init();
    } 
    init() { 
        const { signal } = this.controller; 
        // 所有事件监听器统一管理,爽到飞起    
        window.addEventListener('resize', (e) => {
            clearTimeout(this.resizeTimer);
            this.resizeTimer = setTimeout(
                () => this.handleResize(e), 200);
            }, { signal }); 
        this.container.addEventListener('scroll', (e) => {
            this.handleScroll(e);
        }, { signal, passive:true}); 
        this.container.addEventListener('click', (e) => {
            this.handleClick(e);
        }, { signal }); 
        document.addEventListener('keydown', (e) => {
            if (e.key ==='Delete' && this.selectedRows.length > 0) {
                this.deleteSelectedRows();
            }}, { signal }); 
        this.container.addEventListener('contextmenu', (e) => {
            e.preventDefault();
            this.showContextMenu(e);
        }, { signal });
    } 
    destroy() {
        this.controller.abort();
    }
}

我们经常需要在 js 中处理 dom 的监听和卸载工作。因为addEventListener也能接收signal属性的。我们最后只需要调用controller.abort(),这个controllersignal传递的相关事件监听都会被自动相应卸载了。

参考文章

https://juejin.cn/post/7112699475327615006$ 
https://mp.weixin.qq.com/s/hCzNwr__BIQBpJrpfEW1Vw

关注我 《FE前端指南》,腾讯前端开发工程师带你从零到一了解前端开发。

全部评论

相关推荐

评论
1
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务