- The following figure for 2020/01/05 Redraw during review
- The following figure for 2020/01/05 Redraw during review
- The following figure for 2021/02/07 Then review the request process
Navigation
[[ thorough 01] Execution context ](https://juejin.im/post/684490...)
[[ thorough 02] Prototype chain ](https://juejin.im/post/684490...)
[[ thorough 03] Inherit ](https://juejin.im/post/684490...)
[[ thorough 04] The event loop ](https://juejin.im/post/684490...)
[[ thorough 05] currying Partial function Function memory ](https://juejin.im/post/684490...)
[[ thorough 06] Implicit conversion and Operator ](https://juejin.im/post/684490...)
[[ thorough 07] Browser caching mechanism (http Caching mechanisms )](https://juejin.im/post/684490...)
[[ thorough 08] Front end security ](https://juejin.im/post/684490...)
[[ thorough 09] Depth copy ](https://juejin.im/post/684490...)
[[ thorough 10] Debounce Throttle](https://juejin.im/post/684490...)
[[ thorough 11] Front-end routing ](https://juejin.im/post/684490...)
[[ thorough 12] Front-end modularization ](https://juejin.im/post/684490...)
[[ thorough 13] Observer mode Publish subscribe mode Two way data binding ](https://juejin.im/post/684490...)
[[ thorough 14] canvas](https://juejin.im/post/684490...)
[[ thorough 15] webSocket](https://juejin.im/post/684490...)
[[ thorough 16] webpack](https://juejin.im/post/684490...)
[[ thorough 17] http and https](https://juejin.im/post/684490...)
[[ thorough 18] CSS-interview](https://juejin.im/post/684490...)
[[ thorough 19] Handwriting Promise](https://juejin.im/post/684490...)
[[ thorough 20] Handwritten functions ](https://juejin.im/post/684490...)
[[react] Hooks](https://juejin.im/post/684490...)
[[ Deploy 01] Nginx](https://juejin.im/post/684490...)
[[ Deploy 02] Docker Deploy vue project ](https://juejin.im/post/684490...)
[[ Deploy 03] gitlab-CI](https://juejin.im/post/684490...)
[[ Source code -webpack01- Pre knowledge ] AST Abstract syntax tree ](https://juejin.im/post/684490...)
[[ Source code -webpack02- Pre knowledge ] Tapable](https://juejin.im/post/684490...)
[[ Source code -webpack03] Handwriting webpack - compiler Simple compilation process ](https://juejin.im/post/684490...)
[[ Source code ] Redux React-Redux01](https://juejin.im/post/684490...)
[[ Source code ] axios ](https://juejin.im/post/684490...)
[[ Source code ] vuex ](https://juejin.im/post/684490...)
[[ Source code -vue01] data Response type and Initialize rendering ](https://juejin.im/post/684490...)
[[ Source code -vue02] computed Response type - initialization , visit , The update process ](https://juejin.im/post/684490...)
[[ Source code -vue03] watch Listening properties - Initialization and update ](https://juejin.im/post/684490...)
[[ Source code -vue04] Vue.set and vm.$set ](https://juejin.im/post/684490...)
[[ Source code -vue05] Vue.extend ](https://juejin.im/post/684490...)
[[ Source code -vue06] Vue.nextTick and vm.$nextTick ](https://juejin.im/post/684790...)
Pre knowledge
Some words
Interceptors: Interceptor
bother: annoyance
ties: system , bundle , necktie , contact
immutably: constant
precedence: Priority
determine: determine , find out
( Determine if a value is a FormData Determine whether the value is FormDate type )
inherit: Inherit
<font color=DarkOrchid>axios Cancel request method 1 - axios.CancelToken.source()</font>
const source = axios.CancelToken.source() Factory function
- source.token
- source.cancel(message)
source = {token: cancelToken object , cancel: Cancel function }
- axios.isCancel
Be careful
Every time you request source It can't be the same source object , If it's the same source object , After the cancellation request , The request cannot be sent again
(1) After canceling the request , The request cannot be sent again const CancelToken = axios.CancelToken; const source = CancelToken.source(); // ----------------------------- source Factory function axios.get('/user/12345', { cancelToken: source.token // ------------------------------ token [2021/01/04 to update ] // ------------------------------ token, After canceling the request here , You can't send the request again , Because it's the same source object , Is the same token // ------------------------------ The solution is to source In each request method }).catch(function (thrown) { if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // handle error } }); // Cancel the request source.cancel('Operation canceled by the user.'); // ----------------- cancel function
2021/01/04 When reviewing, I found the above request , You cannot send the request again after canceling the request , Use the following methods to solve
(2) After canceling the request , You can send the request again import React, { useState } from 'react' import axios from 'axios' const AxiosCancelToken = () => { const [cancalFn1, setCancalFn1] = useState(() => { }) as any const [links] = useState([ { name: 'axios Source code - axios Source code analysis warehouse ', url: 'https://github.com/woow-wu7/7-react-admin-ts/tree/master/src/SOURCE-CODE-ANALYSIS/AXIOS' }, { name: 'axios Source code - My Nuggets blog ', url: 'https://juejin.cn/post/6844904147532120072' }, ]) const renderLinks = () => links.map(({ name, url }) => <div key={name}><a href={url} target="blank">{name}</a></div>) // axios Cancel the request // Method 1 // axios.CancelToken.source.token // axios.CancelToken.source.cancel // Be careful : Set up a source It can't be the same source, If it's the same source Words , After canceling the request, you can't re request again const handleRequest = async () => { const source = axios.CancelToken.source() setCancalFn1(() => source.cancel) await new Promise(resolve => { setTimeout(() => { console.log(' Time delay 2s perform ') return resolve('success') }, 2000) }) await axios({ url: '/API/pic.php', method: 'get', cancelToken: source.token }).catch(err => { if (axios.isCancel(err)) { console.log('object :>> ', err.message); } else { console.log('error') } }) } const cancelRequest = () => { cancalFn1(' The request was cancelled ') } return ( <div className="axios-cancel-token"> <p>Axios - CancelToken test </p><br /> <div> Please open the browser debugging panel mode </div> <button onClick={handleRequest}> Click send request 1 - Every request uses promise Time delay 2s simulation </button><br /> <button onClick={cancelRequest}> Click on - Cancel request method 1 - Factory function source</button><br /> <div> {renderLinks()} </div> </div> ) } export default AxiosCancelToken
<font color=DarkOrchid>axios Cancel request method 2 - new axios.CancelToken()</font>
const cancalTokenInstance = new axios.CancelToken(c => cancel = c)
- The generated is cancalToken example
- Parameter is a callback function , The parameters of the callback function are cancel() function
Pay attention to the incoming CancelToken(c => cancel = c) It's a cancel function , This cancel The function takes its own arguments c Assigned to cancel Variable ,c It's also a function , namely cancel function
const CancelToken = axios.CancelToken; let cancel; axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { // An executor function receives a cancel function as a parameter cancel = c; }) }); // cancel the request cancel();
2020/01/06 Review the example of the second cancellation request , There is a hole in it , Look at the comments in the following code
- 1. By direct new axios.CancelToken()
2. Instead of calling CancelToken Static method of source() Generate {token, cancel}
const handleRequest2 = async () => { const token = new axios.CancelToken(c => setCancelFn2(() => c)) // take c Assign a value to state await new Promise(resolve => { setTimeout(() => { console.log(' Time delay 2s perform ') return resolve('success') }, 2000) }) await axios({ url: '/API/pic.php', method: 'get', cancelToken: token, // cancelToken: new axios.CancelToken(c => setCancelFn2(() => c)) // This kind of writing is not allowed !!!!!!!!!!!!!!!!!!!! }).catch(err => { if (axios.isCancel(err)) { console.log('object :>> ', err.message); } else { console.log('error') } }) } const cancelRequest2 = () => { console.log(cancelFn2); cancelFn2(' The request was cancelled ') // state }
<font color=DarkOrchid>axios Cancel the request - example 1</font>
<template>
<div class="cancal-request">
<div>cancel-request</div>
<div @click="getData"> request : Click Get request data - new CancelToken() Method </div>
<div @click="cancalRequest"> Cancel the request : Click Cancel to get the requested data </div>
<div @click="getData2"> request : Click Get request data - source() Factory function mode </div>
<div @click="cancalRequest2"> Cancel the request : Click Cancel to get the requested data </div>
<div>{{data.responseTime}}</div>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "CancelRequest",
data() {
return {
cancelFn: null,
data: {
responseTime: null
},
source: null
};
},
methods: {
getData() {
axios({
baseURL: "/api",
timeout: 2000,
url: "/cancel-request",
method: "get",
cancelToken: new axios.CancelToken(c => {
// new axios.CancelToken()
// Return value => It's a cancelToken
// Parameters => Is a callback function , The parameters of the callback function are cancel() function
this.cancelFn = c;
})
}).then(res => {
console.log(res, "res-data");
this.data = res.data
});
},
cancalRequest() {
if (this.cancelFn) {
this.cancelFn(' With new axios.CancelToken(c => this.cancelFn = c) Way to cancel the request ');
// Cancel the request
}
},
getData2() {
this.source = axios.CancelToken.source();
// utilize axios.CancelToken.source() The factory function generates an object
// source = {token: cancelToken object , cancel: Cancel function }
// source.token
// source.cancel
axios({
baseURL: "/api",
timeout: 2000,
url: "/cancel-request",
method: "get",
cancelToken: this.source.token // token
}).then(res => {
console.log(res, "res-data");
this.data = res.data
});
},
cancalRequest2() {
if (this.source.cancel) {
this.source.cancel(' With source() Factory function method Cancel the request '); // cancel
}
}
}
};
</script>
<font color=DarkOrchid>axios Cancel the request - example 2 - mixin The way </font>
mixin/index.js
import axios from 'axios';
export const mixinCancelToken = {
data() {
return {
cancelToken: null, // ----------------------------------- cancelToken
cancel: null // ----------------------------------------- cacel Cancel function
}
},
created() {
this.cancelToken = new axios.CancelToken(c => { // -------- cancelToken assignment
this.cancel = c // -------------------------------------- cancel Function assignment
})
},
beforeDestroy() {
if (this.cancel) {
this.cancel(` Cancel the request mixin The way `) // --------------------- When the component is uninstalled , Cancel the request
}
}
}
In the business component
<template>
<div class="cancal-request-component">
<div>cancel-request-component</div>
<div @click="getData"> request : Click Get request data - mixin The way </div>
<div @click="cancalRequest"> Cancel the request : Click Cancel to get the requested data - - mixin The way </div>
</div>
</template>
<script>
import { mixinCancelToken } from "../mixin";
import axios from "axios";
export default {
name: "CancelRequestComponent",
mixins: [mixinCancelToken], // ----------------------------------------- Inject mixin
data() {
return {};
},
mounted() {},
methods: {
getData() {
axios({
baseURL: "/api",
timeout: 2000,
url: "/cancel-request",
method: "get",
cancelToken: this.cancelToken // --------------------------------- from mixin In order to get cancelToken
}).then(res => {
console.log(res, "res-data");
this.data = res.data
});
},
cancalRequest() {
if (this.cancel) {
this.cancel(' Cancel the request mixin The way ') // ------------------------------- from mixin In order to get cancel
}
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="stylus">
.cancal-request-component {
background: yellow;
}
</style>
axios Use of interceptors
import axios from 'axios'
const baseURL = '/api'
const headers = {
'Content-Type': 'application/json'
}
const axiosInstance = axios.create({
baseURL,
headers,
timeout: 1000,
})
console.log(typeof axiosInstance, 'axios.create({...}) Returns a function ')
// Request to intercept
// 1. Axios() There is... In the constructor this.interceptors object
// 2. interceptors There are two properties on the object
// request attribute , Is an instance object , Inherited... On the prototype object use eject forEach Such method
// response attribute , Is an instance object , Inherited... On the prototype object use eject forEach Such method
axiosInstance.interceptors.request.use(config => {
return config
}, error => {
return Promise.reject(error)
})
// The response to intercept
axiosInstance.interceptors.response.use(response => {
return response
}, error => {
return Promise.reject(error)
})
export const baseApi = async ({ url, method, paramsOrData }) => {
try {
const paramsOrDataKey = method.match(/get/i) ? 'params' : 'data';
const res = await axios.request({
url,
method,
[paramsOrDataKey]: paramsOrData
})
return res
} catch (err) {
throw new Error(err)
}
}
export default axiosInstance
XMLHttpRequest
How to get response???
- xhr.response
- xhr.responseText
- xhr.responseXML
xhr.responseText
- stay xhr.responseType = 'text' , '', When not set ,xhr This property only exists on the instance object , Only then can I call
xhr.response
- stay xhr.responseType = 'text' ,'' when , The value is ( '' )
- stay xhr.resposneType Other values , The value is ( null )
xhr.responseType
- text
- document
- json
- blob
- arrayBuffer
const xhr = new XMLHttpRequest() // new The command always returns an object , Or this object , Or return The object that follows // new The constructor is called , explain XMLHttpRequest It's a constructor
- initialization HTTP Request parameters , such as url,http Request method, etc , But not ( Don't send requests )
xhr.open() The method is mainly for xhr.send() Methods use
xhr.open(method, url, async, username, password)
Parameters
- method:http Requested method , Include GET POST HEAD
- url: Requested address
async: Asynchronous or not
- true, The default value is , Asynchronous requests , Usually you need to call onreadystatechange() Method
- false, Yes send() Method calls will block , Until the response is fully received
(2) xhr.send()
Send a http request
xhr.send(body)
- get request :get The requested parameters can be written directly in open() In the method
post request :post The requested parameters are written in send() In the method
Be careful :
- body The data type of the parameter affects requestHeader Medium Content-Type The default value of , How to specify it manually will override the default value
- If data yes Document type , It's also HTML Document type , be content-type The default value is text/html;charset=UTF-8; Otherwise application/xml;charset=UTF-8;
- If data yes DOMString type ,content-type The default value is text/plain;charset=UTF-8;
- If data yes FormData type ,content-type The default value is multipart/form-data; boundary=[xxx]
- If data It's other types , Will not be set content-type The default value of
(3) xhr.setRequestHeader()
- To specify a http The head of the request , Only in readState = 1 Can be called
setRequestHeader When you can call
-
- stay readyStaet = 1 when
-
- stay open() After method ,send() Before method
-
- Actually 1 2 It means the same thing
xhr.setRequestHeader('name', 'value')
-
Parameters
- name: The name of the head
- value: The value of the head
Be careful
- setRequestHeader() Methods can be ( Multiple calls ) , It's not worth it ( Cover override ) It is ( Additional append )
- setRequestHeader() Only in readyState = 1 Can be called , namely open() After method ,send() Before method
(4) xhr.getResponseHeader()
Appoint http Value of response header
(5) xhr.abort()
- Cancel current response , Close the connection and end any pending network activity
- xhr.abort() Will readyState Reset to 0
- application : Cancel the request , The request took too long , When the response is no longer necessary , Call the method
abort: It means termination
(6) xhr.onreadystatecange()
- stay readyState Triggered when the state changes
- xhr.onreadystatechange() stay readyState = 3 when , It is possible to call
- onreadystatechange Are all lowercase
readyState hump
readyState state
- UNSENT ------------- xhr Object successfully constructed ,open() Method not called
- OPEND ------------- open() Method is called ,send() Method not called ,setRequestHeader() Can be called
- HEADERS_RECEIVED --- send() Method has been called , The response header and response status have been returned
- LOADING ------------ Response body ( response entity body ) Downloading , In this state, through xhr.response There may already be response data
NODE ------------- The whole data transmission process is over , Whether this request succeeds or fails
(7) xhr.onload
- Trigger when the request is successful , here readyState = 4
Be careful : a key !!!
- 1. In addition to the xhr.onreadystatechange Of the specified callback function readyState = 4 Time value
2. You can also do it in xhr.onload Value in the event
xhr.onload = function() {// The request is successful if (xhr.status === 200 ) { // do successCallback }
}
3. Judge xhr.status === 200 It's pit. , Because the status code returned when successful is not only 200, The following is more reliable
xhr.onload = function () {// If the request succeeds if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) { // 304 not modified The resource was not modified Negotiate the cache // 2 Status code at the beginning , Indicates that the request was successful //do successCallback }
}
(8) xhr.timeout
- Set expiration time
- problem 1: How to determine the start time of the request ? yes ( xhr.onloadstart ) When the event is triggered , That is to say xhr.send() When called
- analysis : because xhr.open() Just created a link , When there is no real data transmission , Only a call xhr.send() The transmission really starts when
- problem 2: When is the request over ?
analysis :( xhr.loadend ) End when the event is triggered
(9) xhr.onprogress Download progress information
(10) xhr.upload.onprogress Upload progress information
xhr.upload.onprogress = function(e) {
if ( e.lengthComputable ) {const present = e.loaded / e.total * 100;
}
}XMLHttpRequest Request case
XMLHttpRequest Request case ---- <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <button id="buttonId"> Click on , Request data </button> <script> const button = document.getElementById('buttonId') button.addEventListener('click', handleClick, false) function handleClick() { const xhr = new XMLHttpRequest() xhr.open('GET', 'http://image.baidu.com/channel/listjson?pn=0&rn=30&tag1= star &tag2= All &ie=utf8', true) // open() Method xhr.setRequestHeader('Content-Type', 'application/json') // setRequestHeader Must be in open() After the method ,send() Call before method , namely readyState === 1 when xhr.responseType = 'text' xhr.timeout = 10000 xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { // Through here this Instead of xhr It's the same thing // because this Determine the point at run time ,xhr Instance is calling onreadystatechange Method , therefore this Point to xhr example console.log(JSON.parse(this.responseText)) // Equivalent to console.log(JSON.parse(xhr.responseText)) } } xhr.onload = function () { if ((xhr.status >= 200 && xhr.status < 300) || (xhr.status === 304)) { console.log(JSON.parse(xhr.responseText), 'xhr.onload It is a callback triggered when the request is completed ') } } xhr.send() // Send a request } </script> </body> </html>
axios
- package.json =>
main: index.js
=> The description entry file is index.js - index.js =>
module.exports = require('./lib/axios');
(1) axios Request process sorting
- createInstance Function signature :
(config) => axios instance
- var axios = createInstance(defaults), Get into createInstance Internal function
- <font color=red size="5">var context = new Axios(defaultConfig)</font>
<font color=red>Axios</font>
function Axios(instanceConfig){...}
It's a constructorexample
defaults
- this.defaults = instanceConfig
interceptors object
request object
new InterceptorManager() Generated instance object
response object
new InterceptorManager() Generated instance object
Axios.prototype
- request
- getUri
- 'delete', 'get', 'head', 'options', 'post', 'put', 'patch' Wait for the method of request
<font color=red>InterceptorManager</font>
InterceptorManager
It's a constructorInterceptorManager Constructors The generated instance has ( An attribute )
handlers
- Is an array
- The members of the array are such objects
{fulfilled: fulfilled, rejected: rejected}
perhapsnull
InterceptorManager.prototype There are ( Three methods )
- use
- eject
- forEach
<font color=red>InterceptorManager.prototype.use - Add interceptor </font>
- Function signature :
(fulfilled, rejected) => (this.handlers.length - 1)
Parameters
- fulfilled
- rejected
Return value
- (this.handlers.length - 1)
- Indicates that the object is in this.handers Subscript in array , It is mainly used for InterceptorManager.prototype.eject Method
The main role :
-
- towards handlers in push
{fulfilled: fulfilled, rejected: rejected}
object
- towards handlers in push
-
- Return to one
(this.handlers.length - 1)
As a way to delete the object ID, In fact, it is the subscript of the member in the array
- Return to one
-
- Function signature :
<font color=red>InterceptorManager.prototype.eject - Delete interceptor </font>
Parameters
- id
- InterceptorManager.prototype.eject(id)
The main role :
- Delete this id Corresponding in handlers The object at that position in the array
- Be careful : This is not the real deletion , I'm going to take this id The corresponding property is set to null, stay forEach During traversal, it will judge and filter out null term
<font color=red>InterceptorManager.prototype.forEach - To filter out null member , Traverse the execution fn(h) </font>
Parameters
- fn
- InterceptorManager.prototype.forEach(fn)
The main role :
- Traverse this.handlers Array , The value in the array is not equal to null Each object of the as fn(h) And execute
Realization :
- Mainly through utils.forEach To achieve
<font color=red>forEach - utils/forEach</font>
- utils.js Medium forEach Method
- forEach(obj, fn)
The main role
- If parameters obj yes null and undefined Go straight back to , Not going down
- If parameters obj yes number,string,boolean,function Then turn it into an array and make the following judgment
If parameters obj It's an array ( namely number,string,boolean,function.array Will traverse the array ), Just loop through the array
fn.call(null, obj[i], i, obj)
i It's an array subscript
If parameters obj It's the object
fn.call(null, obj[key], key, obj);
key It's object's key
<font color=blue>mergeConfig</font>
- Function signature :
(config1, config2) => config
Parameters :
- config1
- config2
return
- config
- That is, the total after the merger config object
- Function signature :
<font color=blue>isObject - utils/isObject</font>
val !== null && typeof val === 'object'
- If not null also typeof Is an object true
<font color=blue>isArray - utils/isArray</font>
Object.prototype.toString.call(val) === '[object Array]'
<font color=blue>merge - utils/merge</font>
- Function signature :
(obj1, obj2, ...) => obj
- Parameters : Object or array , It's better to be a pure object
- Return value : A merged object
principle
-
- Loop through each property of each parameter object
-
- If result The result object is this key Corresponding value It's an object or an array , And the parameter object key Corresponding value It's also an object or array , Just recursively execute merge, Merge the two objects again
-
If result The result object is this key Corresponding value Not an object or array , Just take the... Of the parameter object directly key by key,value by value Assign a value to result object
- So the next object has the same properties , be result The property of the object is reassigned to the value exclusive to the following parameters , It will be reassigned, which is equivalent to overwriting
-
- Function signature :
<font color=blue>deepMerge - utils/deepMerge</font>
and merge The difference between
- When the parameter object 2 One of the properties in is the object , And the parameter object 1 When there is no such attribute in , Still call recursively deepMerge, Instead of assigning directly
The result is :
- deepMerge Returned object , When this property of the parameter object is modified , No effect deepMerge Returned object
- merge Returned object , When this property of the parameter object is modified ,merge The property of the returned object will also change
- namely The difference between a shallow copy and a deep copy
<font color=blue>transformData</font>
- Function signature :
(data, headers, fns) =>
- Function signature :
<font color=blue>getDefaultAdapter</font>
- Return value :adapter
The main role
- Make different requests according to different environments
- browser :XMLHttpRequest request
- node:http https modular
Distinguish between browsers and node Environmental Science
Browser environment
- XMLHttpRequest There is
- There is , load
adapter = require('./adapters/xhr')
node Environmental Science
- process There is
- There is , load
adapter = require('./adapters/http')
<font color=blue size=5>Axios.prototype.get - With get Method examples </font>
- Function signature :
(url, data) => this.request(utils.merge(config||{}, {method: method, url: url}))
- this.request It is on the object where the call is made request => Axios On an instance of request => this.request === Axios.prototype.request
- Function signature :
<font color=blue size=5>Axios.prototype.request</font>
- Axios.prototype.request(config)
(1) If config Is a string , that config = arguments[1] || {};config.url = arguments[0]; otherwise config = config || {};
character string
- config = arguments[1] || {};
- config.url = arguments[0]
axios('example/url'[, config]) It's satisfying config It's a string, be config That's the second parameter , The first parameter is url
object
- config = config || {}
(2) Merge config
- config = mergeConfig(this.defaults, config);
(3) Set up config.method
- If config.method There is , Just turn it into lowercase
- If config.method non-existent , however his.defaults.method There is ,config.method = this.defaults.method.toLowerCase()
- Neither of the above exists , Default config.method = get
(4) var chain = [dispatchRequest, undefined];
- Note that there is no implementation dispatchRequest function
(5) <font color=blue>dispatchRequest</font>
- Function signature :
(config) => adapter(config).then(value => value, reason => Promise.reject(reason))
- Return value : After the request is completed , A new promise
- Function signature :
- (6) according to 45:
var chain = [(config) => adapter(config).then(value => value, reason => Promise.reject(reason)), undefined]
- (7) The interceptor skips
(8) <font color=blue>var promise = Promise.resolve(config)</font>
- take config Conversion of objects promise object , The object is resolve state , Its value will be used as then(value => {}) The parameter of the first successful callback of the method is value Value
(9) this.interceptors.request.forEach(interceptor => { chain.unshift(interceptor.fulfilled, interceptor.rejected) })
- Loop traversal request Medium this.handlers Array , Treat each item as interceptor Parameters ,unshift To chain in
- <font color=blue>chain = [ A successful way to request interceptors , Failure method of request interceptor ,dispatchRequest, undefined]</font>
Special attention should be paid here :
- request: new InterceptorManager()
- response: new InterceptorManager()
<font color=blue>this.handlers In the constructor , Not in the constructor prototype On , namely handles Array on each instance , Are independent of each other </font>
- <font color=blue>axiosInstance.interceptors.request.use and axiosInstance.interceptors.response.use</font>
- <font color=blue> Two use It is independent to different handlers in push and unshift object </font>
- <font color=blue>request yes unsfift in front </font>
- <font color=blue>response yes push rearwards </font>
(10) this.interceptors.response.forEach(interceptor => {chain.push(interceptor.fulfilled, interceptor.rejected)})
- and 9 Empathy
- Loop traversal response Medium handlers Array , Put each item here as forEach The parameters of the callback function
- <font color=blue>chain = [ A successful way to request interceptors , Failure method of request interceptor ,dispatchRequest, undefined, A successful way to respond to interceptors , The failure method of response interceptor ]</font>
(11) while loop chain Array , perform <font color=blue>promise = promise.then(chain.shift(), chain.shift())</font>
- promise Is the merged final configuration object of the incoming request
- Each time from chain Take two elements from the front to the back of the array
- <font color=blue>var chain = [ A successful way to request interceptors , Failure method of request interceptor , (config) => adapter(config).then(value => value, reason => Promise.reject(reason)), undefined, A successful way to respond to interceptors , The failure method of response interceptor ]</font>
- Be careful : there config Namely (8) In the step config Converted into promise object , The object will act as .then The parameter that the method successfully calls back is config
- obtain :
var chain = [ A successful way to request interceptors , Failure method of request interceptor , (config) => adapter(config).then(value => value, reason => Promise.reject(reason)), undefined, A successful way to respond to interceptors , The failure method of response interceptor ] promise = Promise.resolve(config); .then(' Request successfully intercepted 2', ' Request failed to intercept 2') // Pass down config , Be careful 2 stay 1 front ,unshift .then(' Request successfully intercepted 1', ' Request failed to intercept 1') .then(dispatchRequest, undefined) // Actually send the request ,(config) => adapter(config).then(value => value, reason => Promise.reject(reason)) .then(' Response successfully intercepted 1', ' Response failed to intercept 1') .then(' Response successfully intercepted 2', ' Response failed to intercept 2') // Be careful 2 stay 1 Back ,push - (12) Finally back to promise - then() Back to a new promise example - You can go through .then() Method to get the result value of the request
(2) axios Request flow , Interception principle - Source code
1. package.json => main => index.js => equire('./lib/axios')
1. package.json => main => index.js => equire('./lib/axios')
'use strict';
var utils = require('./utils');·
var bind = require('./helpers/bind');
var Axios = require('./core/Axios');
var mergeConfig = require('./core/mergeConfig');
var defaults = require('./defaults');
function createInstance(defaultConfig) {
// createInstance(defaultConfig)
// Parameters : Configuration object
// effect : Create a axios example , And back to
var context = new Axios(defaultConfig);
// Generate axios example context
// Instance attributes
// Instance attributes :defaults = instanceConfig = The parameter object passed in defaultConfig
// Instance attributes : interceptors object
// interceptors.request = new InterceptorManager()
// interceptors.response = new InterceptorManager()
// Instance attributes :handlers An array
// Properties on prototype objects :use eject forEach
// use => towards handlers add to {fulfilled: fulfilled, rejected: rejected} object , Returns an array index , Subscripts are used for eject
// eject => towards handlers Set the subscript member to null, It is equivalent to deleting , Because in forEach Will judge whether it is null
// forEach => loop handlers Array , Proceed accordingly fn(h);
// fn => yes forEach Passed in parameter function
// h => yes handlers Each member of , object 4
// Properties on prototype objects
// request
// getUri
// 'delete', 'get', 'head', 'options', 'post', 'put', 'patch' Wait for the method of request
// summary :
// (1) context There are defaults interceptors request getUri 'delete', 'get', 'head', 'options', 'post', 'put', 'patch' Wait for the method of request
// (2) interceptors.request and interceptors.response There are handlers use eject forEach
var instance = bind(Axios.prototype.request, context);
// bind The method is similar to that of the original sound bind
// here bind Is to return a function warp() => Axios.prototype.request.apply(context, [...arguments])
// therefore instance It's a function , This function will request Medium this Binding in context And return the execution result
// var instance = warp() => Axios.prototype.request.apply(context, [...arguments])
utils.extend(instance, Axios.prototype, context);
// Inherit the prototype
// utils.extend effect :
// take Axios.prototype All properties and methods are assigned to instance
// If it is Axios.prototype The method on the , Try to make this Bound to the context
// Final : instance The method has Axios.prototype All properties and methods on
utils.extend(instance, context);
// Inheritance instance
// take context All properties and methods on are assigned to instance
return instance;
}
var axios = createInstance(defaults);
// establish axios example
axios.Axios = Axios;
// Expose Axios class to allow class inheritance
// Can pass new axios.Axios()
axios.create = function create(instanceConfig) {
return createInstance(mergeConfig(axios.defaults, instanceConfig));
};
// Factory for creating new instances
// summary : establish axios Three methods of example
// 1. axios(configObject)
// axios Itself is such a function warp() => Axios.prototype.request.apply(context, [...arguments])
// 2. axios.create(configObject)
// call createInstance(mergeConfig(axios.defaults, instanceConfig))
// 3. new axios.Axios().request(configObject)
axios.Cancel = require('./cancel/Cancel');
// Cancel(message) It's a constructor
// example
// message
// Prototype
// toString
// _CANCEL = true
axios.CancelToken = require('./cancel/CancelToken');
// CancelToken(executor) It's a constructor
// example
// promise
// reason
// Prototype
// throwIfRequested
// Static methods
// source
// return {token: token, cancel: cancel} Such an object
// new CancelToken(executor)
// This is called in development : cancelToken = new axios.CancelToken(function executor(c) { cancel = c}) !!!!!!!!!!!!!!!!!!!!!!!!!!
// 1. If executor It's not a function , Throw the wrong
// 2. this.promise = One promise object , Always out of pending state , Calling resolve(message) Change state when
// 3. executor The argument to a function is also a function , namely c function
// 4. c The main function is resolve(new Cancel(message)) When executed ,
// 5. stay Axios.prototype.request => dispatchRequest => defaults => getDefaultAdapter
// throwIfCancellationRequested(config)
// If config.cancelToken There is , config.cancelToken.throwIfRequested() namely throw this.reason namely throw new Cancel(message) That is, a {message: 'xxxx'}
// 5. stay Axios.prototype.request => dispatchRequest => defaults => getDefaultAdapter => xhr.js It's all right XMLHttpRequest request
// Will judge config.cancelToken Whether there is
// There is , use abort() Interrupt execution , take resolve(message) Of message As then() Parameters of the callback reject(message)
// 6. summary :
// config.cancelToken = {
// promise: new Promise(function(resolve){
// resolve({ message: '...'})
// }),
// reason: { message: '....' }
// }
axios.isCancel = require('./cancel/isCancel');
// Sign a , Used to mark whether to cancel
// The above three attributes are mainly used to cancel the request
// Expose all/spread
axios.all = function all(promises) {
return Promise.all(promises);
};
axios.spread = require('./helpers/spread');
module.exports = axios;
module.exports.default = axios;
// Allow use of default import syntax in TypeScript
// That is, it can be done through import axios from 'axios' Mode introduction
2. lib/core/Axios.js ---------- Axios There are various request methods in and Axios.prototype.request Method
3. With axios.get('www.baidu.com', {....}) get Request as an example
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
/*eslint func-names:0*/
Axios.prototype[method] = function(url, config) {
return this.request(utils.merge(config || {}, {
method: method,
url: url
}));
};
});
// The code above represents : Traversal array , Add various methods
// such as :Axios.prototype.get = function(url, config) { return request(....) }
// Be careful :this.request() It can only be determined when calling this The direction of , Let's go through axios.get() In this case this Namely axios example
-----
analysis : First look at utils.forEach This function defines
1. Parameters :obj and fn
2. If obj yes null perhaps undefined Directly return to interrupt execution
3. If it is number,string,boolean,function,symbol Just put obj Construct into an array
4. If obj It's an array , That is, through 3 The processed data types are arrays =================> fn.call(null, obj[i], i, obj)
// obj[i] Is the value of the array
// i It's a subscript
// obj It's the original array
5. If obj It's the object ,=================================================> fn.call(null, obj[key], key, obj)
// obj[key] Is the value corresponding to the attribute of the object
// key Is a property of an object
// obj It's the original object
function forEach(obj, fn) {
if (obj === null || typeof obj === 'undefined') {
return;
}
if (typeof obj !== 'object') {
/*eslint no-param-reassign:0*/
obj = [obj];
}
if (isArray(obj)) {
for (var i = 0, l = obj.length; i < l; i++) {
fn.call(null, obj[i], i, obj);
}
} else {
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
fn.call(null, obj[key], key, obj);
}
}
}
}
4. Axios.prototype.request - The core method of the request , Each method calls the method
Axios.prototype.request = function request(config) {
// axios('example/url'[, config]) This situation config It's just one. string
// axios Namely warp() => Axios.prototype.request.apply(context, [...arguments]) Such a function
if (typeof config === 'string') {
config = arguments[1] || {}; // The second parameter is config object
config.url = arguments[0]; // The first parameter is url
} else {
config = config || {};
}
config = mergeConfig(this.defaults, config);
// Merge the passed in parameter object and the default parameter object according to different conditions , Return to the merged config
// Not yet mergeConfig Specific consolidation rules
if (config.method) {
config.method = config.method.toLowerCase();
} else if (this.defaults.method) {
config.method = this.defaults.method.toLowerCase();
} else {
config.method = 'get';
}
// If the incoming config There is method, Use the incoming method And turn to lowercase
// If it doesn't exist , But by default cofig in method There is , Use the default
// It doesn't exist ,get Method
var chain = [dispatchRequest, undefined];
// dispatchRequest --------------------------------------------------------------------------------------- analysis 1
// var chain = [(config) => adapter(config).then(), null]
var promise = Promise.resolve(config);
// The passed in parameter object config Turn into promise object
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
chain.unshift(interceptor.fulfilled, interceptor.rejected);
// towards chain Array loop unshift
// chain = [ A successful way to request interceptors 2, Failure method of request interceptor 2, A successful way to request interceptors 1, Failure method of request interceptor 1, (config) => adapter(config).then(), null]
});
// this.interceptors.request.forEach --------------------------------------------------------------------- analysis 2
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
chain.push(interceptor.fulfilled, interceptor.rejected);
// towards chain Array loop push
// chain = [ A successful way to request interceptors 2, Failure method of request interceptor 2, A successful way to request interceptors 1, Failure method of request interceptor 1, (config) => adapter(config).then(), null, A successful way to respond to interceptors 1, The failure method of response interceptor 1, A successful way to respond to interceptors 2, The failure method of response interceptor 2]
});
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
// Take out two methods at a time from left to right
// promise = Promise.resolve(config);
// .then(' Request successfully intercepted 2', ' Request failed to intercept 2') // Pass down config , Be careful 2 stay 1 front ,unshift
// .then(' Request successfully intercepted 1', ' Request failed to intercept 1')
// .then(dispatchRequest, undefined) // Actually send the request ,(config) => adapter(config).then(value => value, reason => Promise.reject(reason))
// .then(' Response successfully intercepted 1', ' Response failed to intercept 1')
// .then(' Response successfully intercepted 2', ' Response failed to intercept 2') // Be careful 2 stay 1 Back ,push
}
return promise;
// Finally back to promise
// then() Back to a new promise example
// You can go through .then() Method to get the result value of the request
};
analysis 1
dispatchRequest
Function signature :(config) => adapter(config).then()
function throwIfCancellationRequested(config) {
if (config.cancelToken) {
config.cancelToken.throwIfRequested();
}
}
module.exports = function dispatchRequest(config) {
throwIfCancellationRequested(config);
// If config in cancelToken There is , It means cancel the request ,throw this.reason = throw new Cancel(message) = throw {messsage: '...'}
config.headers = config.headers || {};
config.data = transformData(
config.data,
config.headers,
config.transformRequest
);
config.headers = utils.merge(
config.headers.common || {},
config.headers[config.method] || {},
config.headers
);
// tuils.merge(obj1, obj2)
// Loop through all parameter objects , And traverse each attribute of each parameter object
// Assign each attribute and value to the new object
// If the property of the new object and the property of the parameter object are both one object , recursive
// If the new object does not exist or is not an object , Direct assignment
// Pay attention to distinguish between utils.deepMerge and utils.merge
// deepMerge => When the property does not exist in the new object , Parameter object when the property is an object , No direct assignment , It's a shallow copy
utils.forEach(
['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
function cleanHeaderConfig(method) {
delete config.headers[method];
}
);
var adapter = config.adapter || defaults.adapter;
// adapter According to the browser environment or node The environment uses different requests
// browser => XMLHttpRequest To judge => XHR
// node => process To judge => http https
return adapter(config).then(function onAdapterResolution(response) {
// Return the requested data ,promise packing
throwIfCancellationRequested(config);
// If config.cancelToken There is , Terminate response , Throw an error
// Transform response data
response.data = transformData(
response.data,
response.headers,
config.transformResponse
);
return response; // Return the request result and wrap the processed object
}, function onAdapterRejection(reason) {
if (!isCancel(reason)) {
throwIfCancellationRequested(config);
// Transform response data
if (reason && reason.response) {
reason.response.data = transformData(
reason.response.data,
reason.response.headers,
config.transformResponse
);
}
}
return Promise.reject(reason);
});
};
analysis 2
InterceptorManager.prototype.forEach
Parameters :fn
effect : loop handlers Array , Take each member as an argument to the destroy function , The perform fn(h)
Be careful :
1. this.handlers yes [{fulfilled: fulfilled,rejected: rejected}] Such an array Or the members are null
2. axios.interceptors.requet.handlers and axios.interceptors.requet.handlers It's a different array , Because it was executed twice new InterceptorManager() It's a different example
- request: new InterceptorManager()
- response: new InterceptorManager
InterceptorManager.prototype.forEach = function forEach(fn) {
utils.forEach(this.handlers, function forEachHandler(h) {
if (h !== null) {
fn(h);
}
});
};
(3) axios Cancel the request - Source code
Take the use of this cancellation request as an example
const CancelToken = axios.CancelToken; const source = CancelToken.source(); // ----------------------------- source Factory function axios.get('/user/12345', { cancelToken: source.token // --------------------------------------- token }).catch(function (thrown) { if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // handle error } }); // Cancel the request source.cancel('Operation canceled by the user.'); // ----------------- cancel function
1. lib/axios.js
axios.Cancel = require('./cancel/Cancel');
axios.CancelToken = require('./cancel/CancelToken');
axios.isCancel = require('./cancel/isCancel');
2. axios.CancelToken = require('./cancel/CancelToken')
- CancelToken(executor)
CancelToken It's a constructor
example
- promise
- reason
Be careful :<font color=blue> Generated cancelToken The instance is to be mounted to the request method config Object </font>
- like this
axios.get('/user/12345', { cancelToken: source.token }).catch()
'use strict'; var Cancel = require('./Cancel'); function CancelToken(executor) { if (typeof executor !== 'function') { // executor It has to be a function throw new TypeError('executor must be a function.'); } var resolvePromise; this.promise = new Promise(function promiseExecutor(resolve) { resolvePromise = resolve; // this.promise It's a place where pending State of promise object }); var token = this; // Fix this executor(function cancel(message) { // Here is the call executor, When calling, another cancel Function as executor Parameters of // executor It's incoming CancelToken Parameter function // executor Of ( Parameters ) Also a ( function ), namely cancel function // Here is cancel Function definition // cancel The argument to the function is message if (token.reason) { return; } token.reason = new Cancel(message); // this.resaon non-existent , Just new Cancel(message) // new Cancel(message) It's a constructor // example // messge // Prototype // toString // __CANCEL__ = true // therefore // reason It's just one. {message: 'xxxx'} The object of resolvePromise(token.reason); // change this.promise The state of , from pending Turn into resolve }); } // CancelToken summary // CancelToken The instance properties generated by the constructor have two promise and reason // token = { // promise: new Promise(resolve => resolve({message: 'xxxxxxx'})), // reason: {messge: 'xxxxxx'} // } // Be careful : Generated cancelToken The instance is to be mounted to the requested config Object !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // `axios.get('/user/12345', { cancelToken: source.token }).catch()` like this !!!!!!!!!!!!!!!!!!!!!!!!!!!! CancelToken.prototype.throwIfRequested = function throwIfRequested() { // reason There is , Just throw it wrong if (this.reason) { throw this.reason; } }; CancelToken.source = function source() { // Static methods source var cancel; var token = new CancelToken(function executor(c) { // there c Is the above executor The parameters of the function cancel function // c The main function is : hold this.promise The status of the pending Turn into resolve cancel = c; }); return { token: token, // token yes {promise: new Promise(resolve => resolve({messge: ''})), reason: {message: ''}} Such an object cancel: cancel // Cancel function }; }; module.exports = CancelToken;
- like this
(3) When cancel() Function execution time , Request the incoming config Object cancelToken Property object promise The status changes to resolve after , Just enter dispatchRequest
- <font color=blue> Judgment is needed at all stages config in cancelToken Whether there is , There is a mistake , Interrupt execution </font>
- Before request
- When asked
- Wait after response
module.exports = function dispatchRequest(config) {
throwIfCancellationRequested(config);
// Before sending , Will judge config Whether there is cancelToken
// If it exists, throw it wrong , Interrupt execution
config.headers = config.headers || {};
// Transform request data
config.data = transformData(
config.data,
config.headers,
config.transformRequest
);
// Flatten headers
config.headers = utils.merge(
config.headers.common || {},
config.headers[config.method] || {},
config.headers
);
utils.forEach(
['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
function cleanHeaderConfig(method) {
delete config.headers[method];
}
);
var adapter = config.adapter || defaults.adapter;
return adapter(config).then(function onAdapterResolution(response) {
// stay adapter(config) in => default.js in => adapter In folder => Browser environment xhr.js in
// Made the following judgment
// if (config.cancelToken) {
// config.cancelToken.promise.then(function onCanceled(cancel) {
// if (!request) {
// return;
// }
// request.abort();
// reject(cancel);
// request = null;
// });
// }
// Be careful : above config.cancelToken.promise.then() Of promise The change of state is when cancel Function time , become fulfilled state
// in other words : When cancel Function execution time , Only if you ask abort Interrupt request , And throw it wrong
throwIfCancellationRequested(config);
// Please complete , When the corresponding data is obtained , Also judge config Whether there is cancelToken
// If it exists, throw it wrong , Interrupt execution
// Transform response data
response.data = transformData(
response.data,
response.headers,
config.transformResponse
);
return response;
}, function onAdapterRejection(reason) {
if (!isCancel(reason)) {
throwIfCancellationRequested(config);
// Judge when something goes wrong config Whether there is cancelToken
// If it exists, throw it wrong , Interrupt execution
// Transform response data
if (reason && reason.response) {
reason.response.data = transformData(
reason.response.data,
reason.response.headers,
config.transformResponse
);
}
}
return Promise.reject(reason);
});
};
(4) stay dispatchRequest => adapter => xhr in
if (config.cancelToken) {
// If cancelToken There is
config.cancelToken.promise.then(function onCanceled(cancel) {
// When cancel() Function execution time => config.cancelToken.promise The state is already resolve({message: message})
// The perform cancel Function time , the resolve operation
if (!request) {
return;
}
request.abort(); // Interrupt request
reject(cancel); // Throw an error ,message
// Clean up request
request = null;
});
}
Use of examples :axios Cancel request in CancelToken stay vue Application in the project
demand 1
- If there is only one request per page , So you can go through mixin hold token and cancel Injected into each component data in
demand 2
- Every route jump , Cancel all requests of the last route
-
- When the route jumps , Use routing guard beforeEach, perform store All in cancel Cancel function
-
stay axios Your request is being intercepted
- new axios.CancelToken(c => cancel = c) Assign an instance to config.cancelToken attribute ;
- take cancel function push To store Storage in
Project source code - Source analysis address
- axios Source code analysis - Project source address
- Specific folder :
src => SOURCE-CODE-ANALYSIS => AXIOS In the folder
Information
Chuan Shen https://juejin.im/post/684490...
a key cancelToken https://juejin.im/post/684490...
https://juejin.im/post/684490...
axios Official website https://github.com/axios/axios
vue and react How to use axios Cancel the request https://juejin.im/post/684490...