ajax、fetch、axios如何中断请求?

先来说一说“中断请求”的实际场景,当页面有多个tab页签时,每次切换页签都会去请求数据,频繁的切换就会去请求很多次,比如A页签切换到B页签,A页签请求完全是不必要的,这时候可以在切换时中断请求。

AbortController实验室功能,已经在主流浏览器实现,IE浏览器可以不支持,需要导入polyfill。

AbortController接口表示一个控制器对象,允许你根据需要中止一个或多个 Web请求。官网参考:https://developer.mozilla.org...

1、axios

axios已经实现AbortController,具体实践如下:

// fontend
import React, { useEffect } from 'react';
import axios from 'axios';

const AbortController = () => {

  const CancelToken = axios.CancelToken;
  const source = CancelToken.source();

  useEffect(() => {
    axios.post('http://127.0.0.1:8088/getData', {name: 'zs', pwd: '123456'}, {cancelToken: source.token})
      .then(res => {
        console.log(res);
      })
  }, []);

  const abort = () => {
    source.cancel('cancle request!');
  }

  return (
    <>
      <div>this is a abortController page.</div>
      <div><button onClick={abort}>取消请求</button></div>
    </>
  )
}

export default AbortController;
// backend
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser')

const app = express();

app.use(cors({
  origin: '*',
  allowedHeaders: ['content-type'],
}))

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true })); 

app.post('/getData', (req, res) => {
  const {name, pwd} = req.body;
  if (!name || !pwd) {
    return res.json({
      code: 200,
      data: {},
      errorMessage: '参数不能为空'
    })
  }
  setTimeout(() => {
    res.json({
      code: 200,
      data: {name, pwd},
      errorMessage: '参数不能为空'
    })
  }, 5000);
})

app.listen(8088, () => {
  console.log('server is listening 8088');
})

image

早期切换页签时可以将source.cancel()放在useEffect返回函数里面执行,当页签下组件销毁时就取消请求

来看看axios是如何实现请求中断的,大致原理是从外部操作promise流触发then回调中断请求

function CancelToken(executor) {
  if (typeof executor !== 'function') {
    throw new TypeError('executor must be a function.');
  }

  var resolvePromise;
  this.promise = new Promise(function promiseExecutor(resolve) {
    resolvePromise = resolve;
  });

  var token = this;
  executor(function cancel(message) {
    if (token.reason) {
      // Cancellation has already been requested
      return;
    }

    token.reason = new Cancel(message);
    resolvePromise(token.reason);
  });
}

CancelToken.source = function source() {
  var cancel;
  var token = new CancelToken(function executor(c) {
    cancel = c;
  });
  return {
    token: token,
    cancel: cancel
  };
};

c指cancel函数,当执行source.cancel()时从外部去操作CancelToken的私有属性resolvePromise,也就是resolve函数。

详细流程图如下:
axios执行流程图.png

2、fetch

fetch直接使用AbortController来中断请求,详细如下:
首先安装abort-controller库,该库实现了web abort-controller。

yarn add abort-controller -S
import React, { useEffect } from 'react';
import Controller from 'abort-controller';

const AbortController = () => {

  const abortController = new Controller();

  useEffect(() => {
    fetch('http://127.0.0.1:8088/getData', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        name: 'zs',
        pwd: '123456'
      }),
      signal: abortController.signal
    })
  }, []);

  const abort = () => {
    abortController.abort()
  }

  return (
    <>
    <div>this is a abortController page.</div>
    <div><button onClick={abort}>取消请求</button></div>
    </>
  )
}

export default AbortController;

3、$.ajax

$.ajax内部已经实现了abort功能。

ajax(settings?: JQuery.AjaxSettings): JQuery.jqXHR;

interface jqXHR<TResolve = any> extends Promise3<TResolve, jqXHR<TResolve>, never,
        Ajax.SuccessTextStatus, Ajax.ErrorTextStatus, never,
        jqXHR<TResolve>, string, never>,
        Pick<XMLHttpRequest, 'abort' | 'getAllResponseHeaders' | 'getResponseHeader' | 'overrideMimeType' | 'readyState' | 'responseText' |
            'setRequestHeader' | 'status' | 'statusText'>,
        Partial<Pick<XMLHttpRequest, 'responseXML'>> {
        responseJSON?: any;
        // 中断请求
        abort(statusText?: string): void;

        state(): 'pending' | 'resolved' | 'rejected';
        statusCode(map: Ajax.StatusCodeCallbacks<any>): void;
    }

具体例子:

import React, { useEffect } from 'react';
import $ from 'jquery';

const AbortController = () => {

  var abortController: JQuery.jqXHR<any> | null = null;

  useEffect(() => {
    abortController = $.ajax({
      url: 'http://127.0.0.1:8088/getData',
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      data: JSON.stringify({
        name: 'zs',
        pwd: '123456'
      }),
      success: function(res) {
        console.log(res)
      }
    })

  }, []);

  const abort = () => {
    abortController?.abort()
  }

  return (
    <>
    <div>this is a abortController page.</div>
    <div><button onClick={abort}>取消请求</button></div>
    </>
  )
}

export default AbortController;
avatar
记得要微笑
前端工程师

求上而得中,求中而得下,求下而不得

1.7k 声望
4.4k 粉丝
0 条评论
推荐阅读
alicdn边缘节点不稳定导致页面崩溃问题
某工作日,线上某用户向客服专员反馈没法正常访问“查看报价页面”,页面内容没有呈现。客服专员收到反馈后,将问题转交给SRE处理。很奇怪的是,SRE访问生产环境“查看报价页面”显示正常,为了进一步分析定位问题,S...

记得要微笑阅读 739

再也不学AJAX了!(三)跨域获取资源 ① - 同源策略
「再也不学 AJAX 了」是一个以 AJAX 为主题的系列文章,希望读者通过阅读本系列文章,能够对 AJAX 技术有更加深入的认识和理解,从此能够再也不用专门学习 AJAX。本篇文章为该系列的第四篇,最近更新于 2023 年 1...

libinfs19阅读 4k评论 4

封面图
再也不学 AJAX 了!(一)AJAX 概述
跨域获取数据:介绍了与「跨域发送 AJAX 请求」相关的一些内容:例如「同源策略」与四种跨域请求资源的方式:JSONP,CORS,postMessage 和 WebSocket;

libinfs19阅读 3.9k评论 6

封面图
再也不学AJAX了!(二)使用AJAX ② Fetch API
「再也不学 AJAX 了」是一个以 AJAX 为主题的系列文章,希望读者通过阅读本系列文章,能够对 AJAX 技术有更加深入的认识和理解,从此能够再也不用专门学习 AJAX。本篇文章为该系列的第三篇,最近更新于 2023 年 1...

libinfs2阅读 621

封面图
Ajax实现搜索联想 搜索关键词提醒 无刷新搜索
通过javascript监听搜索框的内容,调用后端即可。(1)javascript监听搜索框的内容(2)把搜索框的关键词传给后端进行搜索(3)搜索到结果,遍历到页面

TANKING1阅读 4.4k

如何优雅地封装 axios
如何优雅地封装 axios工作中接手他人的项目,看到一些 axios 封装很是复杂,难用,现在来总结一下 axios 封装 xhr 的问题。在 vue 项目中使用,希望达到下面的效果:引用方便,在组件中,通过 this.$http[method]...

JackZhouMine3阅读 708

封面图
vue页面消毁时,取消axios当面所有请求
取消axios请求,需要了解axios里的 cancelToken 属性 {代码...} 首页定义一个数组进行储存每个请求的cancelToken, 可以储存到Vue对象、vuex、window等,在跳转路由时取消请求即可;路由处理

Max迪丶先生1阅读 3.4k

avatar
记得要微笑
前端工程师

求上而得中,求中而得下,求下而不得

1.7k 声望
4.4k 粉丝
宣传栏