写在前面,准备
安装 node新版本
安装 json-server
以下内容是学习 尚硅谷Web前端axios入门与源码解析 总结的。
1. 认识 axios
2. 为 axios应用准备案例
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>axios基本使用</title>
<link crossorigin="anonymous" href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
</head>
<body>
<div class="container">
<h2 class="page-header">基本使用</h2>
<button class="btn btn-primary"> 发送GET请求 </button>
<button class="btn btn-warning" > 发送POST请求 </button>
<button class="btn btn-success"> 发送 PUT 请求 </button>
<button class="btn btn-danger"> 发送 DELETE 请求 </button>
</div>
<script>
//获取按钮
const btns = document.querySelectorAll('button');
//第一个
btns[0].onclick = function(){
//发送 AJAX 请求
axios({
//请求类型
method: 'GET',
//URL
url: 'http://localhost:3000/posts/2',
}).then(response => {
console.log(response);
});
}
//添加一篇新的文章
btns[1].onclick = function(){
//发送 AJAX 请求
axios({
//请求类型
method: 'POST',
//URL
url: 'http://localhost:3000/posts',
//设置请求体
data: {
title: "今天天气不错, 还挺风和日丽的",
author: "张三"
}
}).then(response => {
console.log(response);
});
}
//更新数据
btns[2].onclick = function(){
//发送 AJAX 请求
axios({
//请求类型
method: 'PUT',
//URL
url: 'http://localhost:3000/posts/3',
//设置请求体
data: {
title: "今天天气不错, 还挺风和日丽的",
author: "李四"
}
}).then(response => {
console.log(response);
});
}
//删除数据
btns[3].onclick = function(){
//发送 AJAX 请求
axios({
//请求类型
method: 'delete',
//URL
url: 'http://localhost:3000/posts/3',
}).then(response => {
console.log(response);
});
}
</script>
</body>
</html>
3. ES6中 Promise的原理和应用
4 创建实例对象
//创建实例对象 /getJoke
const duanzi = axios.create({
baseURL: 'https://api.apiopen.top',
timeout: 2000// 请求超时时间2s
// headers 可以通过在这里设置,也可以在request拦截器里创建
});
const onather = axios.create({
baseURL: 'https://b.com',
timeout: 2000
});
//这里 duanzi 与 axios 对象的功能几近是一样的
// duanzi({
// url: '/getJoke',
// }).then(response => {
// console.log(response);
// });
duanzi.get('/getJoke').then(response => {
console.log(response.data)
})
5. axios的配置
请求配置
这些是创建请求时可以用的配置选项。只有 url
是必需的。如果没有指定 method
,请求将默认使用 get
方法。
{
// `url` 是用于请求的服务器 URL
url: '/user',
// `method` 是创建请求时使用的方法
method: 'get', // default
// `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
// 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
baseURL: 'https://some-domain.com/api/',
// `transformRequest` 允许在向服务器发送前,修改请求数据
// 只能用在 'PUT', 'POST' 和 'PATCH' 这几个请求方法
// 后面数组中的函数必须返回一个字符串,或 ArrayBuffer,或 Stream
transformRequest: [function (data, headers) {
// 对 data 进行任意转换处理
return data;
}],
// `transformResponse` 在传递给 then/catch 前,允许修改响应数据
transformResponse: [function (data) {
// 对 data 进行任意转换处理
return data;
}],
// `headers` 是即将被发送的自定义请求头
headers: {'X-Requested-With': 'XMLHttpRequest'},
// `params` 是即将与请求一起发送的 URL 参数
// 必须是一个无格式对象(plain object)或 URLSearchParams 对象
params: {
ID: 12345
},
// `paramsSerializer` 是一个负责 `params` 序列化的函数
// (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
paramsSerializer: function(params) {
return Qs.stringify(params, {arrayFormat: 'brackets'})
},
// `data` 是作为请求主体被发送的数据
// 只适用于这些请求方法 'PUT', 'POST', 和 'PATCH'
// 在没有设置 `transformRequest` 时,必须是以下类型之一:
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - 浏览器专属:FormData, File, Blob
// - Node 专属: Stream
data: {
firstName: 'Fred'
},
// `timeout` 指定请求超时的毫秒数(0 表示无超时时间)
// 如果请求话费了超过 `timeout` 的时间,请求将被中断
timeout: 1000,
// `withCredentials` 表示跨域请求时是否需要使用凭证
withCredentials: false, // default
// `adapter` 允许自定义处理请求,以使测试更轻松
// 返回一个 promise 并应用一个有效的响应 (查阅 [response docs](#response-api)).
adapter: function (config) {
/* ... */
},
// `auth` 表示应该使用 HTTP 基础验证,并提供凭据
// 这将设置一个 `Authorization` 头,覆写掉现有的任意使用 `headers` 设置的自定义 `Authorization`头
auth: {
username: 'july',
password: 'jjjjjuly'
},
// `responseType` 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
responseType: 'json', // default
// `responseEncoding` indicates encoding to use for decoding responses
// Note: Ignored for `responseType` of 'stream' or client-side requests
responseEncoding: 'utf8', // default
// `xsrfCookieName` 是用作 xsrf token 的值的cookie的名称
xsrfCookieName: 'XSRF-TOKEN', // default
// `xsrfHeaderName` is the name of the http header that carries the xsrf token value
xsrfHeaderName: 'X-XSRF-TOKEN', // default
// `onUploadProgress` 允许为上传处理进度事件
onUploadProgress: function (progressEvent) {
// Do whatever you want with the native progress event
},
// `onDownloadProgress` 允许为下载处理进度事件
onDownloadProgress: function (progressEvent) {
// 对原生进度事件的处理
},
// `maxContentLength` 定义允许的响应内容的最大尺寸
maxContentLength: 2000,
// `validateStatus` 定义对于给定的HTTP 响应状态码是 resolve 或 reject promise 。如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),promise 将被 resolve; 否则,promise 将被 rejecte
validateStatus: function (status) {
return status >= 200 && status < 300; // default
},
// `maxRedirects` 定义在 node.js 中 follow 的最大重定向数目
// 如果设置为0,将不会 follow 任何重定向
maxRedirects: 5, // default
// `socketPath` defines a UNIX Socket to be used in node.js.
// e.g. '/var/run/docker.sock' to send requests to the docker daemon.
// Only either `socketPath` or `proxy` can be specified.
// If both are specified, `socketPath` is used.
socketPath: null, // default
// `httpAgent` 和 `httpsAgent` 分别在 node.js 中用于定义在执行 http 和 https 时使用的自定义代理。允许像这样配置选项:
// `keepAlive` 默认没有启用
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true }),
// 'proxy' 定义代理服务器的主机名称和端口
// `auth` 表示 HTTP 基础验证应当用于连接代理,并提供凭据
// 这将会设置一个 `Proxy-Authorization` 头,覆写掉已有的通过使用 `header` 设置的自定义 `Proxy-Authorization` 头。
proxy: {
host: '127.0.0.1',
port: 9000,
auth: {
username: 'aaaaaaaa',
password: 'bbbbbbb'
}
},
// `cancelToken` 指定用于取消请求的 cancel token
// (查看后面的 Cancellation 这节了解更多)
cancelToken: new CancelToken(function (cancel) {
})
}
响应结构
某个请求的响应包含以下信息
{ // `data` 由服务器提供的响应
data: {},
// `status` 来自服务器响应的 HTTP 状态码
status: 200,
// `statusText` 来自服务器响应的 HTTP 状态信息
statusText: 'OK',
// `headers` 服务器响应的头
headers: {},
// `config` 是为请求提供的配置信息
config: {},
// 'request'
// `request` is the request that generated this response // It is the last ClientRequest instance in node.js (in redirects)
// and an XMLHttpRequest instance the browser
request: {}
}
使用 then
时,你将接收下面这样的响应 :
axios.get('/user/12345')
.then(function(response) {
console.log(response.data);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.config);
});
在使用 catch
时,或传递 rejection callback 作为 then
的第二个参数时,响应可以通过 error
对象可被使用。
默认配置
axios.defaults.method = 'GET';//设置默认的请求类型为 GET
axios.defaults.baseURL = 'http://localhost:3000';//设置基础 URL
axios.defaults.params = {id:100};
axios.defaults.timeout = 3000
6. axios的拦截器应用(interceptors)
如果,请求拦截器 throw '参数出问题了'
,那么响应拦截器会返回失败的Promise,后续,只能走失败的回调。
// 设置请求拦截器
axios.interceptors.request.use(function (config) {
console.log('请求拦截器 成功')
// return config;
throw '参数出问题了'
}
请求拦截器后进先执行,响应拦截器先进先执行
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>拦截器</title>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
</head>
<body>
<script>
// promise 相关
// 设置请求拦截器
axios.interceptors.request.use(function (config) {
console.log('请求拦截器 成功 -1号'),
// 修改config配置中的参数
config.params = {a:100}
return config;
}, function (error) {
console.log('请求拦截器 失败 -1号');
return Promise.reject(error);
});
axios.interceptors.request.use(function (config) {
console.log('请求拦截器 成功 -2号')
// 修改config配置中的参数
config.timeout = 5000
// return config;
}, function (error) {
console.log('请求拦截器 失败 -2号');
return Promise.reject(error);
});
// 设置响应拦截器
axios.interceptors.response.use(function (response) {
console.log('响应拦截器 成功 -1号');
return response.data;
}, function (error) {
console.log('响应拦截器 失败 -1号')
return Promise.reject(error);
});
axios.interceptors.response.use(function (response) {
console.log('响应拦截器 成功 -2号');
return response;
}, function (error) {
console.log('响应拦截器 失败 -2号')
return Promise.reject(error);
});
// 发送请求
axios({
method: 'GET',
url: 'http://localhost:3000/posts'
}).then(response => {
console.log('自定义回调处理成功的结果');
console.log(response)
}).catch(reason => {
console.log('自定义失败的回调')
})
</script>
</body>
</html>
7. 请求取消
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>取消请求</title>
<link crossorigin='anonymous' href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
</head>
<body>
<div class="container">
<h2 class="page-header">axios取消请求</h2>
<button class="btn btn-primary"> 发送请求 </button>
<button class="btn btn-warning" > 取消请求 </button>
</div>
<script>
//获取按钮
const btns = document.querySelectorAll("button")
// 声明全局变量
let cancel = null;
// 发送请求
btns[0].onclick = function(){
// 检测上一次的请求是否已经完成
if(cancel !== null){
// 取消上一次请求
cancel()
}
axios({
method:'GET',
url:'http://localhost:3000/posts',
// 1.添加配置对象的属性
cancelToken: new axios.CancelToken(function(f){
// 将 f的值赋给cancel
cancel = f
})
}).then(response =>{
console.log(response)
// 将cancel的值初始化设置
cancel = null
})
}
// 绑定第二个按钮,取消请求
btns[1].onclick = function(){
cancel()
}
</script>
</body>
</html>
8 Axios 源码解析
Axios 对象 创建过程
模拟实现了Axios 即可以当 函数使用,又可以使用原型上的方法。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title> axios 的由来</title>
<!-- <script src="./node_modules/axios/dist/mine-axios.js"></script> -->
</head>
<body>
<script>
// console.log(axios);
//构造函数
function Axios(config){
//初始化
this.defaults = config;//为了创建 default 默认属性
this.intercepters = {
request: {},
response: {}
}
}
//原型添加相关的方法
Axios.prototype.request = function(config){
console.log('发送 AJAX 请求 请求的类型为 '+ config.method);
}
Axios.prototype.get = function(config){
return this.request({method: 'GET'});
}
Axios.prototype.post = function(config){
return this.request({method: 'POST'});
}
//3 声明函数
function createInstance(config){
//实例化一个对象
let context = new Axios(config)//此时,可以用context.get(),context.post() 方法,但是不能当做函数使用context()X
//创建请求函数
let instance = Axios.prototype.request.bind(context)//instance 是一个函数,可以instance({})
//将Axios.prototype 对象中方法添加到instance函数对象中去
Object.keys(Axios.prototype).forEach(key =>{
instance[key] = Axios.prototype[key].bind(context)//this.default this.interceptors 的方法可以使用
})
//为instance 函数对象添加属性, default 与 interceptor
Object.keys(context).forEach(key =>{
instance[key] = context[key]
})
return instance;
}
let axios = createInstance()
//发送请求
axios.get({})
axios.post({})
</script>
</body>
</html>
Axios 发送请求过程
原理是:
axiso的request 请求 --> dispatch --> xhr 的信息,
然后信息由xhr --> dispatch -->request 路径传回,最终返回到
response =>{
console.log(response)
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>请求发送过程</title>
<!-- <script src="./node_modules/axios/dist/mine-axios.js"></script> -->
</head>
<body>
<script>
// axios 发送请求 axios 发送请求的源头是在 Axios.prototype.request bind
// 0 声明构造函数
function Axios(config) {
this.config = config;
}
Axios.prototype.request = function(config){
//1 发送请求
// 其实,在1.1之前合并了config ,这些操作就暂不展示
// 1.1创建一个 promise对象
let promise = Promise.resolve(config)
console.log(promise)
//声明一个数字
let chains = [dispatchRequest,undefined]//undefined 作用就是占位,上原型链的知识
// 调用then方法 指定回调
let result = promise.then(chains[0],chains[1])//由于前面的Promise是成功的回调,所以这里调用的一定是chains[0],也就是dispatchRequest
// let result = promise.then(console.log('dispatchRequest函数'),chains[1]) //这里与上面是一个意思
// 返回 promise 的结果
return result//这里result是返回给Axios.prototype.request的执行结果,(这里的request与下面的axios函数是等效的),也就是返回给下面的axios函数,再由axios.then方法去执行,就拿到结果,
}
//2 dispatchRequest 函数,
/* 这里,如果AJAX发送请求成功,则进行正确的回调,返回response的结果,进一步的, let result = promise.then(chains[0],chains[1])中的结果就是chains[0],,也就是dispatchRequest,也就是response成功的结果值
如果失败,则进行失败的回调,throw error
*/
function dispatchRequest(config) {
//2.1调用适配器发送请求
return xhrAdapter(config).then(response => {
console.log(response)//相当于 console.log('xhrAdapter 函数执行')
// 2.2 对响应的结果进行转换处理
return response
},Error =>{
throw error
})
}
// 3 adapter适配器
function xhrAdapter(config) {
console.log('xhrAdapter 函数执行')
return new Promise((resolve,reject)=>{
// 发送ajax请求
let xhr = new XMLHttpRequest()
// 初始化
xhr.open(config.method,config.url)
// 发送
xhr.send()
// 绑定事件
xhr.onreadystatechange = function () {
if(xhr.readyState === 4){
// 判断成功条件
if(xhr.status >= 200 && xhr.status < 300){
//成功状态
config: config//配置对象
data: xhr.response//响应体
header: xhr.getAllResponseHeaders()//响应头;字符串,对头信息进行解析
request: xhr//xhr请求对象
status: xhr.status//响应状态码
statusText: xhr.statusText//响应状态字符串
}else{
// 失败的状态
reject(new Error('请求失败 失败的状态码为' + xhr.status))
}
}
}
})
}
// 4 创建axios函数
let axios = Axios.prototype.request.bind(null)
axios({
method:'GET',
url:'http://localhost:3000/posts'
}).then(response => {
console.log(response);
});
</script>
</body>
</html>
axios拦截器功能模拟实现
axios拦截器 返回结果的顺序为
原理: 由于Axios.js中 会依次取出 chain 的回调函数,并执行,如下图,chain的长度是10,shift方法是向前添加,会打乱顺序。
axios.interceptors.request.use中.use
方法只是将回调的结果保存在request的handers属性中。
在执行时,对函数进行分组,以跳板的形式,一组一组的,将请求拦截器放前面,响应拦截器放后面。
当都是成功的回调时,跳板形式(红线路径):
当有失败的回调时,跳板形式(蓝线路径):
如果是undefined,那么穿透,继续下一个回调。
拦截器实现原理:
-
通过拦截器管理工具保存回调,
-
request 负责整合,先讲发送请求的dispatchRequest和undefined 放一起,undefined起到重要作用,作为正常跳转的保障,然后将请求拦截器往前放,将响应拦截器往后放,最后整合为一个数组。
-
接着遍历数组,通过promise中链条的方式去执行里面回调。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>拦截器</title>
<!-- <script src="./node_modules/axios/dist/mine-axios.js"></script> -->
</head>
<body>
<script>
//构造函数
function Axios(config) {
this.config = config;
this.interceptors = {
request:new InterceptorManager(),
response:new InterceptorManager()
}
}
// 发送请求 ☆ 重点部分
Axios.prototype.request =function (config) {
// 创建promise对象
let promise = Promise.resolve(config);
// 创建一个数字
const chains = [dispatchRequest,undefined]
// 处理拦截器 将请求拦截器的回调, 压入到 chains 的前面,request.handles = []
this.interceptors.request.handlers.forEach(item => {
chains.unshift(item.fulfilled,item.rejected)
})
// console.log(chains)
/*此时,数组就发生了变化:
0: ƒ two(config)
1: ƒ two(error)
2: ƒ one(config)
3: ƒ one(error)
4: ƒ dispatchRequest(config)
5: undefined
length: 6
*/
// 响应拦截器
this.interceptors.response.handlers.forEach(item => {
chains.push(item.fulfilled,item.rejected)
})
// console.log(chains)
/*此时,数组又就发生了变化:
0: ƒ two(config)
1: ƒ two(error)
2: ƒ one(config)
3: ƒ one(error)
4: ƒ dispatchRequest(config)
5: undefined
6: ƒ (response)
7: ƒ (error)
8: ƒ (response)
9: ƒ (error)
length: 10
*/
// // 到这里数组准备完毕,下面开始遍历
while(chains.length > 0){
promise = promise.then(chains.shift(),chains.shift())//先拿回请求拦截器2号,
}
return promise
}
//发送请求
function dispatchRequest(config) {
// 返回promise对象
return new Promise((resolve,reject)=>{
resolve({
status:200,
statusText:'OK'
})
})
}
// 创建实例
let context = new Axios({})
// 创建axios函数
let axios = Axios.prototype.request.bind(context)
// 将context 属性,包括 config 、interceptors 添加到 axios函数对象上
Object.keys(context).forEach(key =>{
axios[key] = context[key]
})
// console.dir(axios)
// 拦截器管理器 构造函数
function InterceptorManager() {
this.handlers = [];
}
InterceptorManager.prototype.use = function (fulfilled,rejected) {
this.handlers.push({
fulfilled,
rejected
})
}
//1 以下为功能测试代码
// 1.1设置请求拦截器 config 配置对象
axios.interceptors.request.use(function one(config) {
console.log('请求拦截器 成功 - 1号');
return config;
}, function one(error) {
console.log('请求拦截器 失败 - 1号');
return Promise.reject(error);
});
axios.interceptors.request.use(function two(config) {
console.log('请求拦截器 成功 - 2号');
return config;
}, function two(error) {
console.log('请求拦截器 失败 - 2号');
return Promise.reject(error);
});
// 1.2 设置响应拦截器
axios.interceptors.response.use(function (response) {
console.log('响应拦截器 成功 1号');
return response;
}, function (error) {
console.log('响应拦截器 失败 1号')
return Promise.reject(error);
});
axios.interceptors.response.use(function (response) {
console.log('响应拦截器 成功 2号')
return response;
}, function (error) {
console.log('响应拦截器 失败 2号')
return Promise.reject(error);
});
//1.3发送请求
axios({
method: 'GET',
url: 'http://localhost:3000/posts'
}).then(response => {
console.log(response);
});
</script>
</body>
</html>
Axios 取消请求工作 模拟实现
Axios 取消请求原理:
就是在 cancelToken 上维护了一个promise属性,然后将改变promise的 resolvePromise 变量暴露到全局,也就是用一个变量cancel 去改变它的值,只有执行cancel(),内部的resolvePromise 就执行,
于是 resolve 执行,
接着,this.promise 状态就变成功,然后,
config.cancelToken.promise.then(value =>{
xhr.abort()
} )
中的回调就执行,就执行力xhr.abort()
,因此,请求取消。
//cancelToken构造函数
function cancelToken(executor) {
//声明一个变量
var resolvePromise
// 为实例对象添加属性
this.promise = new Promise((resolve) =>{
//将resolve 赋值给 resolvePromise
resolvePromise = resolve
})
//调用 executor 函数
executor(function(){
//执行 resolvePromise 函数
resolvePromise()
})
}
cancelToken构造函数 中的 executor 就是 CancelToken中的
function(c){
// 3 将 c 赋值给 cancel
cancel = c;
}
,红线标注的。
另外,function(c) 中的c 是 虚拟参数 ,实参是executor 函数中的function 。(蓝线标注的)
现在开始取消请求操作
if(config.cancelToken){
// 对 cancelToken 对象中 的 promise对象 指定成功的回调,也就是
/*
new CancelToken(function(c){ cancel = c; });
*/
config.cancelToken.promise.then(value =>{
xhr.abort()
} )
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>取消请求</title>
<link crossorigin='anonymous' href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<!-- <script src="./node_modules/axios/dist/mine-axios.js"></script> -->
</head>
<body>
<div class="container">
<h2 class="page-header">axios取消请求</h2>
<button class="btn btn-primary"> 发送请求 </button>
<button class="btn btn-warning"> 取消请求 </button>
</div>
<script>
//构造函数
function Axios(config){
this.config = config;
}
//原型 request 方法
Axios.prototype.request = function(config){
return dispatchRequest(config);
}
//dispatchRequest 函数
function dispatchRequest(config){
return xhrAdapter(config);
}
//xhrAdapter
function xhrAdapter(config){
//发送 AJAX 请求
return new Promise((resolve, reject) => {
//实例化对象
const xhr = new XMLHttpRequest();
//初始化
xhr.open(config.method, config.url);
//发送
xhr.send();
//处理结果
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
//判断结果
if(xhr.status >= 200 && xhr.status < 300){
//设置为成功的状态
resolve({
status: xhr.status,
statusText: xhr.statusText
});
}else{
reject(new Error('请求失败'));
}
}
}
//关于取消请求的处理
if(config.cancelToken){
//对 cancelToken 对象身上的 promise 对象指定成功的回调
config.cancelToken.promise.then(value => {
xhr.abort();
//将整体结果设置为失败
reject(new Error('请求已经被取消'))
});
}
})
}
//创建 axios 函数
const context = new Axios({});
const axios = Axios.prototype.request.bind(context);
//CancelToken 构造函数
function CancelToken(executor){
//声明一个变量
var resolvePromise;
//为实例对象添加属性
this.promise = new Promise((resolve) => {
//将 resolve 赋值给 resolvePromise
resolvePromise = resolve
});
//调用 executor 函数
executor(function(){
//执行 resolvePromise 函数
resolvePromise();
});
}
//获取按钮 以上为模拟实现的代码
const btns = document.querySelectorAll('button');
//2.声明全局变量
let cancel = null;
//发送请求
btns[0].onclick = function(){
//检测上一次的请求是否已经完成
if(cancel !== null){
//取消上一次的请求
cancel();
}
//创建 cancelToken 的值
let cancelToken = new CancelToken(function(c){
cancel = c;
});
axios({
method: 'GET',
url: 'http://localhost:3000/posts',
//1. 添加配置对象的属性
cancelToken: cancelToken
}).then(response => {
console.log(response);
//将 cancel 的值初始化
cancel = null;
})
}
//绑定第二个事件取消请求
btns[1].onclick = function(){
cancel();
}
</script>
</body>
</html>