如何取消 HTTP 请求

作者: CondorHero | 来源:发表于2021-02-03 18:24 被阅读0次

前言:如何取消 HTTP 请求?这也算一个经典的面试题了,平时工作中大家应该很少用到。但是某些时候还是挺有用的。

比如,在按钮不防抖的情况下,快速连续点击一个按钮,会造成重复发送请求,不用防抖技术如何停止请求。再比如一个请求等待时间过长,用户不想等待了,直接点击取消按钮,取消发送这个请求。

我们知道,浏览器能发送请求,靠两个重要的 API,一个是比较老旧的 XHR,另一个比较新的 Fetch。发送请求的取消针对的也就是这两个。

先来用 NodeJS 来搭个后端,搞一个接口,源代码如下:

const bodyParser = require("body-parser");
const express = require("express");
const path = require("path");
const app = express();
function resolvePath(dir) {
    return path.join(__dirname, dir);
}
app.use(express.static(resolvePath("/public")));
// https://expressjs.com/en/4x/api.html#req.body
app.use(bodyParser.json({ limit: "50mb" }));
app.use(bodyParser.urlencoded({ extended: true }));


app.get("/upload", function (req, res) {
    res.json({
        code: 0,
        message: "GET 发送成功"
    });
});

const port = 8888;
app.listen(port, function () {
    console.log(`listen port ${port}`);
});

后端代码,直接使用 node 命令启动就行了,启动的时候,我们静态化了一个文件夹 —— public,这文件夹下有个文件 index.html,这样我们服务一旦启动完成,只需要访问 http://localhost:8888/ 就能在线调试了,接下来的代码演示,都会在 index.html 中进行,而且只展示 <script></script> 标签部分。

一、XHR 中断请求

XHR 请求的中断是通过 xhr.abort(); 来完成的。

const xhr = new XMLHttpRequest(),
    method = "GET",
    url = "/upload";
xhr.open(method, url, true);

xhr.send({ age: 90 });
xhr.onreadystatechange = (state) => {

    if (xhr.readyState === 4 && (xhr.status === 200)) {
        // do something
        console.log(xhr.responseText);
    }
}

xhr.abort();

前端代码执行之后,network 面板的显示 upload 接口如下:

二、Fetch 中断请求

先看前端代码:

const controller = new AbortController();
const signal = controller.signal;
console.log(signal, "signal的初始状态");
signal.addEventListener("abort", function (e) {
    console.log(signal, "signal的中断状态");
});


fetch("/upload", {signal})
.then((res) => {
    console.log(res, "请求成功");
}).catch(function (thrown) {
    console.log(thrown);
});
// 增加部分结束
controller.abort({
    name: "CondorHero",
    age: 19
});

再看网络请求:

fetch 通过 controller.abort 中断请求传入的形参不能被很好的接收。看控制台我们就能看出来了,完全给忽略了,这点没有 Axios 好:

AbortController 还有一个更加头疼的问题,就是完全不兼容 IE。

三、Axios 请求中断

Axios 中断请求有两种办法,详情见 cancellation,我们直接实战:

第一种:

const CancelToken = axios.CancelToken;
let cancel;
const instance = axios.create();
// Add a request interceptor
axios.interceptors.request.use(function (config) {
    // Do something before request is sent
    return config;
}, function (error) {
    // Do something with request error
    console.log(`request error`, error);
    return Promise.reject(error);
});

// Add a response interceptor
axios.interceptors.response.use(function (response) {
    // Any status code that lie within the range of 2xx cause this function to trigger
    // Do something with response data
    return response;
}, function (error) {
    // Any status codes that falls outside the range of 2xx cause this function to trigger
    // Do something with response error
    return Promise.reject(error);
});
instance.get("/upload", {
    cancelToken: new CancelToken(function executor(c) {
        // An executor function receives a cancel function as a parameter
        cancel = c;
    })
}).then(res => {
    console.log(res);
}).catch(function (thrown) {
    if (axios.isCancel(thrown)) {
        console.log('Request canceled', thrown.message);
    } else {
        // handle error
    }
});
cancel({
    name: "CondorHero",
    age: 19
});

请求直接未发出,最棒的是还能自定义错误:

第二种:

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

const instance = axios.create();
// Add a request interceptor
axios.interceptors.request.use(function (config) {
    // Do something before request is sent
    return config;
}, function (error) {
    // Do something with request error
    console.log(`request error`, error);
    return Promise.reject(error);
});

// Add a response interceptor
axios.interceptors.response.use(function (response) {
    // Any status code that lie within the range of 2xx cause this function to trigger
    // Do something with response data
    return response;
}, function (error) {
    // Any status codes that falls outside the range of 2xx cause this function to trigger
    // Do something with response error
    return Promise.reject(error);
});
instance.get("/upload", {
    cancelToken: source.token
}).then(res => {
    console.log(res);
}).catch(function (thrown) {
    if (axios.isCancel(thrown)) {
        console.log('Request canceled', thrown.message);
    } else {
        // handle error
    }
});
source.cancel({
    name: "CondorHero",
    age: 19
});

浏览器运行结果同第一种。

四、手把手封装 axios 取消重复请求

有中断请求这个技术,当然要用到项目里面去,本来我还想自己写这个教程的,结果看到一篇非常棒的博客,非常 Nice:手把手封装 axios 取消重复请求 成功滑水掉😂。

记住核心思想就一条,维护一个数组,请求 push,响应 filter,重复请求 cancel 掉。

五、最后

最近没活,但是挺闹心,本来二月份约的办港卡,但是因为 Velo 没开户成功,导致富途没入金成功,然后约不了二月份的。只能等下个月的了,但是因为我从一月十八就开始开户了,到现在都没成功,有点着急,还好,中午第二次打电话给客服,告诉我再提交申请下就行了。果然,我填完资料提交瞬间开户成功,这次的男客服比上次的女客服靠谱多了,大赞(好奇都 21 年了,还有男客服,不管了,反正这个客服很棒,一百分💯好评)。

而且我发现汇率是个神奇的东西,前段时间的港币兑成人命币发现我少了将近三百现大洋😢。这还不算这次往 Velo 里面充钱找的中介,又干掉六百五,算了算了,不算了。事能办成就行了。

拜拜~

当前时间 Wednesday, February 3, 2021 18:24:40

相关文章

网友评论

    本文标题:如何取消 HTTP 请求

    本文链接:https://www.haomeiwen.com/subject/pkujnctx.html