|
axios 中一个请求取消的示例:
axios 取消请求的示例代码
import React, { useState, useEffect } from "react";
import axios, { AxiosResponse } from "axios";
export default function App() {
const [index, setIndex] = useState(0);
const [imgUrl, setImgUrl] = useState("");
useEffect(() => {
console.log(</span>loading ${<span class="pl-smi">index</span>}<span class="pl-pds">);
const source = axios.CancelToken.source();
axios
.get("https://dog.ceo/api/breeds/image/random", {
cancelToken: source.token
})
.then((res: AxiosResponse<{ message: string; status: string }>) => {
console.log(</span>${<span class="pl-smi">index</span>} done<span class="pl-pds">);
setImgUrl(res.data.message);
})
.catch(err => {
if (axios.isCancel(source)) {
console.log(err.message);
}
});
<span class="pl-k">return</span> () <span class="pl-k">=></span> {
<span class="pl-c1">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">`</span>canceling ${<span class="pl-smi">index</span>}<span class="pl-pds">`</span></span>);
<span class="pl-smi">source</span>.<span class="pl-en">cancel</span>(<span class="pl-s"><span class="pl-pds">`</span>canceling ${<span class="pl-smi">index</span>}<span class="pl-pds">`</span></span>);
};
}, [index]);
return (
<div>
<button
onClick={() => {
setIndex(index + 1);
}}
>
click
</button>
<div>
<img src={imgUrl} alt="" />
</div>
</div>
);
}

axios 中一个请求取消的示例
通过解读其源码不难实现出一个自己的版本。Here we go...
Promise 链与拦截器
这个和请求的取消其实关系不大,但不妨先来了解一下,axios 中如何组织起来一个 Promise 链(Promise chain),从而实现在请求前后可执行一个拦截器(Interceptor)的。
简单来说,通过 axios 发起的请求,可在请求前后执行一些函数,来实现特定功能,比如请求前添加一些自定义的 header,请求后进行一些数据上的统一转换等。
用法
首先,通过 axios 实例配置需要执行的拦截器:
axios.interceptors.request.use(function (config) {
console.log('before request')
return config;
}, function (error) {
return Promise.reject(error);
});
axios.interceptors.response.use(function (response) {
console.log('after response');
return response;
}, function (error) {
return Promise.reject(error);
});
然后每次请求前后都会打印出相应信息,拦截器生效了。
下面编写一个页面,放置一个按钮,点击后发起请求,后续示例中将一直使用该页面来测试。
import React from "react";
import axios from "axios";
export default function App() {
const sendRequest = () => {
axios.interceptors.request.use(
config => {
console.log("before request");
return config;
},
function(error) {
return Promise.reject(error);
}
);
<span class="pl-smi">axios</span>.<span class="pl-smi">interceptors</span>.<span class="pl-smi">response</span>.<span class="pl-en">use</span>(
<span class="pl-v">response</span> <span class="pl-k">=></span> {
<span class="pl-c1">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">"</span>after response<span class="pl-pds">"</span></span>);
<span class="pl-k">return</span> <span class="pl-smi">response</span>;
},
<span class="pl-k">function</span>(<span class="pl-v">error</span>) {
<span class="pl-k">return</span> <span class="pl-c1">Promise</span>.<span class="pl-c1">reject</span>(<span class="pl-smi">error</span>);
}
);
<span class="pl-en">axios</span>({
url: <span class="pl-s"><span class="pl-pds">"</span><a href="https://dog.ceo/api/breeds/image/random" rel="noreferrer noopener" class="rgh-linkified-code"><span class="pl-pds"></span>https://dog.ceo/api/breeds/image/random</a><span class="pl-pds">"</span></span>,
method: <span class="pl-s"><span class="pl-pds">"</span>GET<span class="pl-pds">"</span></span>
}).<span class="pl-c1">then</span>(<span class="pl-v">res</span> <span class="pl-k">=></span> {
<span class="pl-c1">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">"</span>load success<span class="pl-pds">"</span></span>);
});
};
return (
<div>
<button onClick={sendRequest}>click me</button>
</div>
);
}
点击按钮后运行结果:
before request
after response
load success
拦截器机制的实现
实现分两步走,先看请求前的拦截器。
请求前拦截器的实现
Promise 的常规用法如下:
new Promise(resolve,reject);
假如我们封装一个类似 axios 的请求库,可以这么写:
interface Config {
url: string;
method: "GET" | "POST";
}
function request(config: Config) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(config.method, config.url);
xhr.onload = () => {
resolve(xhr.responseText);
};
xhr.onerror = err => {
reject(err);
};
xhr.send();
});
}
除了像上面那个直接 new 一个 Promise 外,其实任意对象值都可以形成一个 Promise,方法是调用 Promise.resolve,
Promise.resolve(value).then(()=>{ /**... */ });
这种方式创建 Promise 的好处是,我们可以从 config 开始,创建一个 Promise 链,在真实的请求发出前,先执行一些函数,像这样:
function request(config: Config) {
return Promise.resolve(config)
.then(config => {
console.log("interceptor 1");
return config;
})
.then(config => {
console.log("interceptor 2");
return config;
})
.then(config => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(config.method, config.url);
xhr.onload = () => {
resolve(xhr.responseText);
};
xhr.onerror = err => {
reject(err);
};
xhr.send();
});
});
}
将前面示例中 axios 替换为我们自己写的 request 函数,示例可以正常跑起来,输出如下:
interceptor 1
interceptor 2
load success
这里,已经实现了 axios 中请求前拦截器的功能。仔细观察,上面三个 then 当中的函数,形成了一个 Promise 链,在这个链中顺次执行,每一个都可以看成一个拦截器,即使是执行发送请求的那个 then。
于是我们可以将他们抽取成三个函数,每个函数就是一个拦截器。
function interceptor1(config: Config) {
console.log("interceptor 1");
return config;
}
function interceptor2(config: Config) {
console.log("interceptor 2");
return config;
}
function xmlHttpRequest<T>(config: Config) {
return new Promise<T>((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(config.method, config.url);
xhr.onload = () => {
resolve(xhr.responseText as any);
};
xhr.onerror = err => {
reject(err);
};
xhr.send();
});
}
接下来要做的,就是从 Promise 链的头部 Promise.resolve(config) 开始,将上面三个函数串起来。借助 Monkey patch 这不难实现:
function request<T = any>(config: Config) {
let chain: Promise<any> = Promise.resolve(config);
chain = chain.then(interceptor1);
chain = chain.then(interceptor2);
chain = chain.then(xmlHttpRequest);
return chain as Promise<T>;
}
然后,将上面硬编码的写法程式化一下,就实现了任意个请求前拦截器的功能。
扩展配置,以接收拦截器:
interface Config {
url: string;
method: "GET" | "POST";
interceptors?: Interceptor<Config>[];
}
创建一个数组,将执行请求的函数做为默认的元素放进去,然后将用户配置的拦截器压入数组前面,这样形成了一个拦截器的数组。最后再遍历这个数组形成 Promise 链。
function request<T = any>({ interceptors = [], ...config }: Config) {
// 发送请求的拦截器为默认,用户配置的拦截器压入数组前面
const tmpInterceptors: Interceptor<any>[] = [xmlHttpRequest];
interceptors.forEach(interceptor => {
tmpInterceptors.unshift(interceptor);
});
let chain: Promise<any> = Promise.resolve(config);
tmpInterceptors.forEach(interceptor => (chain = chain.then(interceptor)));
return chain as Promise<T>;
}
使用:
执行结果:
interceptor 2
interceptor 1
load success
注意这里顺序为传入的拦截器的反序,不过这不重要,可通过传递的顺序来控制。
响应后拦截器
上面实现了在请求前执行一序列拦截函数,同理,如果将拦截器压入到数组后面,即执行请求那个函数的后面,便实现了响应后的拦截器。
继续扩展配置,将请求与响应的拦截器分开:
interface Config {
url: string;
method: "GET" | "POST";
interceptors?: {
request: Interceptor<Config>[];
response: Interceptor<any>[];
};
}
更新 request 方法,请求前拦截器的逻辑不变,将新增的响应拦截器通过 push 压入数组后面:
function request<T = any>({
interceptors = { request: [], response: [] },
...config
}: Config) {
const tmpInterceptors: Interceptor<any>[] = [xmlHttpRequest];
interceptors.request.forEach(interceptor => {
tmpInterceptors.unshift(interceptor);
});
interceptors.response.forEach(interceptor => {
tmpInterceptors.push(interceptor);
});
let chain: Promise<any> = Promise.resolve(config);
tmpInterceptors.forEach(interceptor => (chain = chain.then(interceptor)));
return chain as Promise<T>;
}
类似 interceptor1 interceptor2,新增两个拦截器用于响应后执行,
function interceptor3<T>(res: T) {
console.log("interceptor 3");
return res;
}
function interceptor4<T>(res: T) {
console.log("interceptor 4");
return res;
}
测试代码:
request({
url: "https://dog.ceo/api/breeds/image/random",
method: "GET",
interceptors: {
request: [interceptor1, interceptor2],
response: [interceptor3, interceptor4]
}
}).then(res => {
console.log("load success");
});
运行结果:
interceptor 2
interceptor 1
interceptor 3
interceptor 4
load success
不难看出,当我们发起一次 axios 请求时,其实是发起了一次 Promise 链,链上的函数顺次执行。
request interceptor 1
request interceptor 2
...
request
response interceptor 1
response interceptor 2
...
因为拉弓没有回头箭,请求发出后,能够取消的是后续操作,而不是请求本身,所以上面的 Promise 链中,需要实现 request 之后的拦截器和后续回调的取消执行。
request interceptor 1
request interceptor 2
...
request
#
- Axios源码深度剖析 - 替代$.ajax,成为xhr的新霸主
前戏 在正式开始axios讲解前,让我们先想想,如何对现有的$.ajax进行简单的封装,就可以直接使用原声Promise了? let axios = function(config){ return ...
- Axios源码分析
Axios是一个基于promise的HTTP库,可以用在浏览器和node.js中. 文档地址:https://github.com/axios/axios axios理解和使用 1.请求配置 { // ...
- Axios源码深度剖析
Axios源码深度剖析 - XHR篇 axios 是一个基于 Promise 的http请求库,可以用在浏览器和node.js中,目前在github上有 42K 的star数 分析axios - 目录 ...
- 一比一还原axios源码(零)—— 概要
从vue2版本开始,vue-resource就不再被vue所维护和支持,官方也推荐使用axios,所以,从我使用axios至今,差不多有四五年了,这四五年的时间只能算是熟练应用,很多内部的实现和原理不 ...
- 一比一还原axios源码(一)—— 发起第一个请求
上一篇文章,我们简单介绍了XMLHttpRequest及其他可以发起AJAX请求的API,那部分大家有兴趣可以自己去扩展学习.另外,简单介绍了怎么去读以及我会怎么写这个系列的文章,那么下面就开始真正的 ...
- 一比一还原axios源码(三)—— 错误处理
前面的章节我们已经可以正确的处理正确的请求,并且通过处理header.body,以及加入了promise,让我们的代码更像axios了.这一章我们一起来处理ajax请求中的错误. 一.错误处理 首先我 ...
- 一比一还原axios源码(四)—— Axios类
axios源码的分析,到目前为止,算上第0章已经四章了,但是实际上,还都没有进入axios真正的主线,我们来简单回顾下.最开始我们构建了get请求,写了重要的buildURL方法,然后我们处理请求体请 ...
- axios源码解析 - 请求拦截器
axios请求拦截器,也就是在请求发送之前执行自定义的函数. axios源码版本 - ^0.27.2 (源码是精简版) 平时在业务中会这样去写请求拦截器,代码如下: // 创建一个新的实例 var s ...
- 从express源码中探析其路由机制
引言 在web开发中,一个简化的处理流程就是:客户端发起请求,然后服务端进行处理,最后返回相关数据.不管对于哪种语言哪种框架,除去细节的处理,简化后的模型都是一样的.客户端要发起请求,首先需要一个标识 ...
随机推荐
- POJ 3207 Ikki's Story IV - Panda's Trick 2-sat模板题
题意: 平面上,一个圆,圆的边上按顺时针放着n个点.现在要连m条边,比如a,b,那么a到b可以从圆的内部连接,也可以从圆的外部连接.给你的信息中,每个点最多只会连接的一条边.问能不能连接这m条边,使这 ...
- Ryuji doesn't want to study 2018徐州icpc网络赛 树状数组
Ryuji is not a good student, and he doesn't want to study. But there are n books he should learn, ea ...
- gulp的介绍和手动安装
gulp, 前端自动化工具, 文件操作, 项目上线之前,将碎片文件合并,将ES6转成ES5,文件压缩,快速搭建服务器... gulp基于node环境 gulp就是node的一个非内置的小模块 gulp ...
- flex布局 一侧固定宽度 一侧自适应
想实现一个类似知乎个人主页的资料显示布局 类是下面这样 其中显示图片在一个div,Name和class和button在一个div中,使用justify-content: space-between; ...
- HTTPS页面使用CNZZ统计代码,Chrome显示警告怎么办?
很多站长会遇到一个问题,网站加入CNZZ的JS统计代码后,Chrome浏览器出现警告:阻止跨站解析器阻断脚本通过document.write调用(A parser-blocking, cross si ...
- Kubernetes --- 详细介绍和架构详解
Kubernetes是一个跨主机集群的开源的容器调度平台,它可以自动化应用容器的部署,扩展和操作,提供以容器为中心的基础架构 目录 一. Kubernetes用途 二. Kubernetes特点 三. ...
- 019 模块2-time库的使用
目录 一.概述 二.time库基本介绍 2.1 time库概述 三.时间获取 四.时间格式化 4.1 格式化控制符 4.2 时间格式化-代码 五.程序计时应用 5.1 程序计时 六.单元小结 参考:h ...
- 搭建自己的技术博客系列(一)使用 hexo 搭建一个精美的静态博客
1.安装 Git 和 nodejs https://hexo.io/zh-cn/docs/
- InnoDB在MySQL默认隔离级别下解决幻读
1.结论 在RR的隔离级别下,Innodb使用MVVC和next-key locks解决幻读,MVVC解决的是普通读(快照读)的幻读,next-key locks解决的是当前读情况下的幻读. 2.幻读 ...
- 下一个排列(Leetcode31)解读
本题代码来自Leetcode官方,个人对其理解后,生成自己的注解,以便更好的让读者理解.如有侵权,立即删除! public class Main31 { public static void main ...
|