AbortController使用场景探索

AbortController是一个DOM API。MDN上对它的介绍是 AbortController接口表示一个控制器对象,允许根据需要终止一个或多个Web请求。

但是它只能用来终止请求吗,那当然不是?

目前笔者只发现的2个AbortController的使用场景,分别是:fetchaddEventListener,下文分别介绍这两种情况。

fetch

fetch提供了一个可选参数signal,可以用来终止请求。
那signal是啥?是怎么终止的?表现形式是啥?真的取消请求了还是只是不接受返回的数据?

下面通过一个案例来实践一下:


按钮.png

页面上提供了2个按钮,分别用来fetch(请求)和abort(废弃请求)。然后通过给fetch添加signal来控制请求:

  const controller = new AbortController();   // 新建一个AbortController实例
  let signal = controller.signal;    // signal是AbortController实例的属性

  const downloadBtn = document.querySelector('.download');
  const abortBtn = document.querySelector('.abort');

  downloadBtn.addEventListener('click', fetchVideo);

  abortBtn.addEventListener('click', function () {
      controller.abort();  // 调用abort方法
      console.log('Download aborted');
  })

  function fetchVideo() {
    //...
    fetch(url, {signal}).then(function(response) {
      //...
    }).catch(function(e) {
      console.error('e', e);
    })
}

点击下载按钮,请求发起,此时请求状态是 待处理(pending)

下载中.png

请求还在进行中时,点击废弃按钮(实际上是调用了AbortController实例的abort方法),状态变成 已取消(canceled):

废弃.png

取消后,fetch请求的promise 将 reject 一个名为 AbortError 的 DOMException,也就是会被例子中的catch捕获到,可以在控制台看到如下错误:

abort error.png

需要注意的是已经abort的请求是不能重复调用的,看fetch源码的实现就可以知道为什么,AbortSignal 在 abort 之后,状态就变成了 aborted,那么下次调用 fetch 请求的时候,当读到 AbortSignal 的状态是 aborted 的时候,就直接 reject 。

   // fetch仓库地址: https://github.com/github/fetch/blob/master/fetch.js 
    
    if (request.signal && request.signal.aborted) {
      return reject(new DOMException('Aborted', 'AbortError'))
    }
fetch after abort .png

也就是说在业务上,如果有同一个请求发了多次只想保留最后一个的话,我们需要用多个controller来控制,每发送一个请求就把上次的请求abort。

addEventListener

AbortController还可以作为addEventListener第三个参数options上的可选属性:


image.png

按照mdn的说法,当调用abort()后,监听器会被移除。其实这就相当于给我们内置了一个移除监听器的方式,可以不用把callback单独提取出成一个函数。

测试

用一个测试按钮,添加监听器,然后在监听函数中使用setTimeout执行abort


addEventListener.png

在点击测试监听按钮前,可以在元素里找到监听器:

before Click.png

点击测试按钮,控制台输出'监听',同时自动移除元素的监听器(需要先关闭控制台重新开):


after abort.png

众所周知,如果需要 removeEventListenr(type, callback), 它的callback必须和addEventListener是同一个函数引用,而在某些业务场景下,我们并不想多写函数可以改成用signal来控制。
例如,当在按钮鼠标时设置一个监听器,在监听器中再监听鼠标移动,鼠标松开关闭监听器:

  document.addEventListener('mousedown', callback);
  document.addEventListener('mouseup', callback2);
  function callback (e) {
      document.addEventListener('mousemove',  callback3);
   }

  function callback2 (e) {
     document.removeEventListener('mousemove', callback3);
  }
  
  function callback3(event) {}

如果改写成用AbortController怎么写呢?

    const controller = new AbortController();
    function callback (e) {
      document.addEventListener('mousemove',  (e) => {
          
      },{
           signal: controller.signal  
      });
   }
    document.addEventListener('mousedown', callback);
    document.addEventListener('mouseup', controller.abort);

总结

AbortController可以用在fetch和addEventListener,分别用来废弃请求和废弃监听器。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容