fetch() 请求终止

循环了6个框,每个框里有一个测试按钮和停止按钮. 怎么点击停止时,终止当前的fetch()请求



```javascript
<el-row :gutter="20" class="jianju" style="margin-left: 0px">
      <el-col
        :span="6"
        :xs="24"
        :sm="12"
        :md="12"
        :lg="8"
        :xl="7"
        class="bianju"
        v-for="(item, index) in content"
        :key="index"
      >
        <div class="grid-content bg-purple cont">
          <el-button
            class="tijiao"
            type="primary"
            @click="ceshi(index, item.type1)"
            >{{ item.name }}
          <el-button
            class="tijiao"
            style="float: right"
            type="warning"
            @click="ceshi(index)"
            >停止
          <div class="neirong">{{ item.value }}div>
        div>
      el-col>
    el-row>



```javascript
ceshi(index) {
      const controller = new AbortController();
      const signal = controller.signal;

      this.content[index].value = "";

      let data = {
        signal: signal,
        method: "post",
        headers: {
          "Content-type": "application:/x-www-form-urlencoded:charset=UTF-8",
        },
        body: JSON.stringify({ request: "stream" }),
      };
      let url = " /api/serial" + (index + 1);
      var self = this;
      var f = (r) => {
        return r.read().then((result) => {
          var data = String.fromCharCode.apply(null, result.value);
          self.content[index].value += data;
          if (!result.done) f(r);
        });
      };

      fetch(url, data)
        .then((r) => r.body.getReader())
        .then(f);

      // setTimeout(() => {
      //   controller.abort();
      //   console.log(signal.aborted);
      // }, 5000);
    },


到目前为止,在浏览器里用 JavaScript 发起请求的方式有两种:XMLHttpRequest 和 fetch。XMLHttpRequest 我们比较熟悉,存在很长时间了,而 fetch 是 ES6 才引入的。

XMLHttpRequest 是可以中途取消的,如下所示:

let xhr = new XMLHttpRequest();xhr.method = 'GET';xhr.url = 'https://slowmo.glitch.me/5000';xhr.open(method, url, true);xhr.send();// 随后取消这个请求abortButton.addEventListener('click', function() {  xhr.abort();});

fetch 刚开始引入的时候并不支持终止操作。最早的关于终止 fetch 请求的 GitHub issue 是在 2015 年提出的。在 fetch 规范之外,有不少解决这个问题的尝试,比如 cancelable-promises 和其他 hack 手段。

但是现在我们终于有了通用的AbortController 和 AbortSignal API。这些 API 是由 DOM 标准而不是语言本身提供的。

img


DOM 规范文档里是这样说的:

虽然 promise 没有内置的中断机制,很多使用它的 API 需要这个终止语义。AbortController就是通过提供abort() 方法切换相应 AbortSignal 对象的状态,从而满足这种需求的。想要支持终止功能的 API 可以接受一个AbortSignal 对象,然后根据它的状态决定如何继续。

// 创建 AbortController 实例const controller = new AbortController();const signal = controller.signal;// 监听 abort 信号, controller.abort() 执行时触发回调signal.addEventListener('abort', () => { console.log(signal.aborted); // true});// 稍后调用中止方法,发出信号controller.abort();
AbortController 用法
fetch 接受 AbortSignal作为第二个参数的一部分。

const controller = new AbortController();const signal = controller.signal;// 5s 后请求收到响应,注意第二个参数fetch('https://slowmo.glitch.me/5000', { signal }) .then(r => r.json()) .then(response => console.log(response)) .catch(err => { if (err.name === 'AbortError') { console.log('Fetch was aborted'); } else { console.error('Oops!', err); } });// 2秒后取消请求setTimeout(() => { controller.abort();}, 2000);
终止 fetch 会同时终止请求和响应,并抛出new DOMException('Aborted', 'AbortError')错误。另外,同一个 AbortSignal对象(上例中的signal)可以用于取消多个fetch请求。

img


可以看出,终止 fetch 是通过发送事件(信号)的方式实现的。

还是用全局变量解决了问题.