如何取消 HTTP 请求

前言:如何取消 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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容