Handwritten axios source code series one: axios core knowledge points

Insert image description here

I recently built a small vue project from scratch and wanted to use axios as a third-party library for the request interface. As a result, after using axios for so long, I wanted to encapsulate axios, which is the src/utils/request.js file in most projects, but I couldn't write it out. I didn't know how to use APIs like create and interceptors; so I decided to dig into the axios source code to find out what axios is.

The current axios source code version is"axios": "^1.3.5"

core functions of axios

1. axios function object

Why is it called axios function object here?

This is because in JavaScript, functions are also objects and can also be attached to properties and methods. As the saying goes: everything is an object.

How to use axios:

  • axios({ method: "post" })
  • axios.post()

It can be seen from the way axios is used that axios can be called and can also use the dot syntax of the object to execute the request method; soaxios is both a function and an object< a i=2>.

Let’s take an example:

// 声明axios函数
function axios(config) {
    
    
	console.log("配置项:", config)
}
// 为axios挂载方法create
axios.create = () => {
    
    
	console.log("axios.create方法")
}
// 为axios挂载属性get
axios.get = "get"

axios({
    
     method: "post" })
axios.create()
console.log("axios的属性:", axios.get)

Insert image description here

We can also print axios:

console.dir(axios)

Insert image description here
You can see that axios is a function and has the create method and get attribute mounted on it.

If you look at the source code of axios, you will know that axios is essentially a function. It just copies the prototype object method and instance object method of the Axios class to the axios function; there are also some other attributes and methods that are also mounted on the axios function object. , such as CancelToken class, Axios class, etc.

2. dispatchRequest sends a request

There are two request methods in axios, and different request methods are used according to different platforms:

  1. xhr (XMLHttpRequest), also known as AJAX. (browser)
  2. http(Nodejs)

Let’s just explore the browser side.

When using axios to send an http request, there is actually only one method called at the bottom, which is the Axios.prototype.request method. The Axios.prototype.request method also executes the dispatchRequest method, and the method is used in the dispatchRequest method to distinguish whether Send request using xhr or http. adapters.getAdapter

flow chart:
Insert image description here

dispatchRequestThe module is where the request is actually sent:

function dispatchRequest(config) {
    
    
  // 根据适配器获取发送请求的方式时xhr还是http
  const adapter = adapters.getAdapter(config.adapter || defaults.adapter);
  // 返回promise对象
  return adapter(config).then(
    response => {
    
    
      return response
    },
    err => {
    
    
      throw new Error(err)
    })
}

3. interceptors interceptors

In projects, when sending http requests, some special processing will be performed on the request and response: judging token, setting request headers, uniformly processing response error information, etc. It would be too troublesome to process each request one by one. For convenience, axios provides interceptors.

Interceptors are divided into:

  • request interceptoraxios.interceptors.request
  • response interceptoraxios.interceptors.response

Use of request interceptor:

axios.interceptors.request.use(function (config) {
    
    
	// 设置token
	const token = sessionStorage.getItem("token");
	if (token) {
    
     config.headers.token = token };
	//返回config配置项
	return config;
}, function (error) {
    
    
	return Promise.reject(error);
})

Use of response interceptor:

axios.interceptors.response.use(function (response) {
    
    
	// 全局统一设置错误信息提示,code、msg是与后台约定好的返回值
	const {
    
     code, msg } = response.data;
	if (code === 0){
    
     
		return response.data;
	} else {
    
    
		Notify({
    
     type: 'danger', msg, duration: 5000 });
		return Promise.reject(new Error(msg || 'Error'));
	}
}, function (error) {
    
    
	const {
    
     message} = error.response.data;
	Notify({
    
     type: 'danger', message, duration: 5000 });
	return Promise.reject(error);
})

Multiple interceptors can be set, for example:

// 请求拦截器1
axios.interceptors.request.use(function one(config){
    
    
	console.log("请求拦截器 1");
	return config;
},function one(error){
    
    
	return Promise.reject(error);
})
// 请求拦截器2
axios.interceptors.request.use(function two(config){
    
    
	console.log("请求拦截器 2");
	return config;
},function two(error){
    
    
	return Promise.reject(error);
})
// 响应拦截器1
axios.interceptors.response.use(function one(response){
    
    
	console.log("响应拦截器 1");
	return response;
},function one(error){
    
    
	return Promise.reject(error);
})
// 响应拦截器2
axios.interceptors.response.use(function two(response){
    
    
	console.log("响应拦截器 2");
	return response;
},function two(error){
    
    
	return Promise.reject(error);
})

When I send a request, the results are printed out in the following order:

请求拦截器 2
请求拦截器 1
响应拦截器 1
响应拦截器 2
response

Why does print first请求拦截器 2 and then print请求拦截器 1?

This involves the execution of the interceptor in the source code.

The source code Axios.prototype.request method defines an execution chain chain = [dispatchRequest, undefined]. Then get the request interceptor, use array.unshift() to add it to the head of the execution chainchain; get the response interceptor, use array.push() Adding it to the end of the execution chain forms the final execution sequence:

chain = [
	two(config),
	two(error),
	one(config),
	one(error),
	dispatchRequest,  // 发送请求
	undefined,
	one(response),
	one(error),
	two(response),
	two(error),
]

4. cancelToken cancel request

The request can be sent or canceled. The cancellation request uses the xhr.abort() method, and abort means 中止.

Cancel request | Axios Chinese documentation | Axios Chinese website

There are two ways to cancel a request:

  1. Use CancelToken (officially deprecated in axios version0.22.0, but still compatible and can still be used)

    const CancelToken = axios.CancelToken;
    let source = CancelToken.source();
    axios.get(url, {
          
          
    	cancelToken: source.token
    }).then(response => {
          
          
    	// ...
    })
    // 取消请求
    source.cancel();
    
  2. Use AbortController (Officially recommended usage, and very simple)

    const controller = new AbortController();
    axios.get(url, {
          
          
    	signal: controller.signal
    }).then(response => {
          
          
    	// ...
    })
    // 取消请求
    controller.abort();
    

    The AbortController interface represents a controller object that allows you to abort one or more web requests as needed

    The picture below shows the AbortController class:

    Insert image description here
    It can be seen that abort is a method of the prototype object of the AbortController class, and signal is an instance object generated by the AbortSignal class.

In the axios source code, the cancellation request encapsulates a CancelToken class, and the publish/subscribe mode is used for unified and centralized processing of cancellation requests. When AJAX sends a request, the onCanceled method of the cancellation request is subscribed. The promise object is used inside the CancelToken to expose the resolve method to the outside. When executing source.cancel() to cancel the request, the internally exposed method is actually executed. resolve method, resolve is executed, the status of the promise changes from pending to fulfilled, and then the promise.then() method is executed, and the subscribed onCanceled method is executed in promise.then().

// xhr.js
const xhr = new XMLHttpRequest();
onCanceled = () => {
    
    
	xhr.abort();
}
config.cancelToken.subscribe(onCanceled)
// CancelToken.js
class CancelToken {
    
    
	constructor(executor){
    
    
		let resolvePromise;
		this._listeners = [];
		this.promise = new Promise(resolve => {
    
    
			resolvePromise = resolve;  // 向外暴露resolve方法
		})
		// executor执行器函数执行,传入参数为一个函数
		executor(function c(){
    
    
			resolvePromise();  // 执行resolve方法
		})
		this.promise.then(()=>{
    
    
			let len = this._listeners.length;
			while(len-- > 0){
    
    
				this._listeners[i](); // 执行onCanceled()
			}
		})
	}
	subscribe(fn){
    
    
		this._listeners.push(fn);  //收集依赖
	}
}

It doesn’t matter if you don’t understand it, because in my opinion, the cancellation request logic is the most confusing and difficult to understand knowledge point in the axios source code, because knowledge points such as 设计模式、闭包、promise异步编程、高阶函数 are used here. As long as one of the knowledge points is vague, it will probably be a bit troublesome, so you can only learn more and read more.

Show a picture to understand:
Insert image description here

Guess you like

Origin blog.csdn.net/ThisEqualThis/article/details/130289192