前言:如何取消 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
网友评论