面试官不要再问我axios了?我能手写简易版的axios

2022-12-16 程序员资讯 0 1,050
¥ 2.88B

包年VIP免费升级包年VIP

开通VIP尊享优惠特权
立即下载 升级会员

axios做为他们组织工作中的常见的ajax允诺库,做为后端技师的他们总之是想一探到底,axios到底是怎样去构架整座架构,尾端的圣夫龙、转接器、 中止允诺那些都是他们时常采用的。

序言

虽然axios源代码许多并非很关键的方式,所以许多方式为的是考量相容性,并没考量到用es6 的句法去写。第一集主要就是快来剖析axios的主要就业务流程,他用es6改写莫雷县axios

圣夫龙

转接器

中止允诺

圣夫龙

三个axios示例上有三个圣夫龙,三个是允诺圣夫龙, 接着积极响应圣夫龙。他们下看下官方网站的用语:

加进圣夫龙

// 加进允诺圣夫龙axios.interceptors.request.use(function

(config) {

// 在推送允诺以后做些甚么 return

config;

}, function

(error) {

// 对允诺严重错误做些甚么 return Promise

.reject(error);

});

去除圣夫龙

const myInterceptor = axios.interceptors.request.use(function () {/*…*/

});

axios.interceptors.request.eject(myInterceptor);

其实源代码中就是,所有圣夫龙的执行 所以说肯定有三个forEach方式。

思路理清楚了,现在他们就开始去写吧。代码我就直接发出来,接着我在下面注解。

export class InterceptorManager

{

constructor

() {

// 存放所有圣夫龙的栈 this

.handlers = []

}

use

(fulfilled, rejected) {

this

.handlers.push({

fulfilled,

rejected,

})

//返回id 便于中止 return this.handlers.length – 1

}

// 中止三个圣夫龙 eject

(id) {

if (this

.handlers[id]) {

this.handlers[id] = null

}

}

// 执行栈中所有的hanlder forEach

(fn) {

this

.handlers.forEach((item) => {

// 这里为的是过滤已经被中止的圣夫龙,因为已经中止的圣夫龙被置null if

(item) {

fn(item)

}

})

}

}

圣夫龙这个类他们已经初步实现了,现在他们去实现axios 这个类,还是先看下官方文档,先看用语,再去分析。

axios(config)

// 推送 POST 允诺

axios({

method: post

,

url: /user/12345

,

data: {

firstName: Fred

,

lastName: Flintstone

}

});

axios(url[, config])// 推送 GET 允诺(默认的方式)

axios(/user/12345);

Axios 这个类最核心的方式其实还是 request 这个方式。他们先看下实现吧

class Axios

{

constructor

(config) {

this

.defaults = config

this

.interceptors = {

request:new

InterceptorManager(),

response: new

InterceptorManager(),

}

}

// 推送三个允诺 request

(config) {

// 这里呢其实就是去处理了 axios(url[,config]) if (typeof config == string

) {

config = arguments[1

] || {}

config.url = arguments[0

]

} else

{

config = config || {}

}

// 默认get允诺,并且都转成小写 if

(config.method) {

config.method = config.method.toLowerCase()

} else

{

config.method = get

}

// dispatchRequest 就是推送ajax允诺 const chain = [dispatchRequest, undefined

]

// 发生允诺以后加入拦截的 fulfille 和reject 函数 this

.interceptors.request.forEach((item) => {

chain.unshift(item.fulfilled, item.rejected)

})

// 在允诺之后增加 fulfilled 和reject 函数 this

.interceptors.response.forEach((item) => {

chain.push(item.fulfilled, item.rejected)

})

// 利用promise的链式调用,将参数一层一层传下去 let promise = Promise

.resolve(config)

//接着我去遍历 chain while

(chain.length) {

// 这里不断出栈 直到结束为止

promise = promise.then(chain.shift(), chain.shift())

}

return

promise

}

}

这里其实就是体现了axios设计的巧妙, 维护三个栈结构 + promise 的链式调用 实现了 圣夫龙的功能, 可能有的小伙伴到这里还是并非很能理解,我还是给大家画三个草图去模拟下这个过程。

假设我有1个允诺圣夫龙handler和1个积极响应圣夫龙handler

一开始他们栈中的数据就三个

面试官不要再问我axios了?我能手写简易版的axios

这个没甚么问题,虽然有圣夫龙的存在,如果存在的话,那么他们就要往这个栈中加数据,允诺圣夫龙顾名思义要在允诺以后所以是unshift。加完允诺圣夫龙他们的栈变成了这样

面试官不要再问我axios了?我能手写简易版的axios

没甚么问题,接着允诺结束后,他们又想对允诺之后的数据做处理,所以积极响应拦截的数据自然是push了。这时候栈结构变成了这样:

面试官不要再问我axios了?我能手写简易版的axios

接着遍历整座栈结构,每次出栈都是一对出栈, 因为promise 的then 就是 三个成功,三个失败嘛。遍历结束后,返回经过所有处理的promise,接着你就可以拿到最终的值了。

adapter

Adapter: 英文解释是转接器的意思。这里我就不实现了,我带大家看一下源代码。adapter 做了一件事非常简单,就是根据不同的环境 采用不同的允诺。如果用户自定义了adapter,就用config.adapter。否则就是默认是default.adpter.

var

adapter = config.adapter || defaults.adapter;

return

adapter(config).then() …

继续往下看deafults.adapter做了甚么事情:

function getDefaultAdapter

() {

var

adapter;

if (typeofXMLHttpRequest !==undefined

) {

// For browsers use XHR adapter adapter = require(./adapters/xhr

);

} else if (typeof process !== undefined && Object.prototype.toString.call(process) === [object process]

) {

// For node use HTTP adapter adapter = require(./adapters/http

);

}

return

adapter;

}

其实就是做个选择:如果是浏览器环境:就是用xhr 否则就是node 环境。判断process是否存在。从写代码的角度来说,axios源代码的这里的设计可扩展性非常好。有点像设计模式中的转接器模式, 因为浏览器端和node 端 推送允诺其实并不一样, 但是他们不关键,他们不去管他的内部实现,用promise包一层做到对外统一。所以 他们用axios 自定义adapter 器的时候, 一定是返回三个promise。ok允诺的方式我在下面模拟写出。

cancleToken

我首先问大家三个问题,中止允诺原生浏览器是怎么做到的?有三个abort 方式。可以中止允诺。那么axios源代码肯定也是运用了这一点去中止允诺。现在浏览器其实也支持fetch允诺, fetch可以取消允诺?许多同学说是不可以的,其实并非?fetch 结合 abortController 可以实现中止fetch允诺。他们看下例子:

const controller = new

AbortController();

const

{ signal } = controller;

fetch(“http://localhost:8000”

, { signal }).then(response => {

console.log(`Request 1 is complete!`

);

}).catch(e => {

console.warn(`Fetch 1 error: ${e.message}`

);

});

// Wait 2 seconds to abort both requestssetTimeout(() => controller.abort(), 2000

);

但是这是个实验性功能,可恶的ie。所以他们这次还是用原生的浏览器xhr基于promise简单的封装一下。代码如下:

export function dispatchRequest

(config) {

return new Promise

((resolve, reject) => {

const xhr = new

XMLHttpRequest()

xhr.open(config.method, config.url)

xhr.onreadystatechange = function

() {

if (xhr.status >= 200 && xhr.status <= 300 && xhr.readyState === 4

) {

resolve(xhr.responseText)

} else

{

reject(失败了

)

}

}

if

(config.cancelToken) {

// Handle cancellation config.cancelToken.promise.then(function onCanceled

(cancel) {

if

(!xhr) {

return

}

xhr.abort()

reject(cancel)

// Clean up request xhr = null

})

}

xhr.send()

})

}

Axios 源代码里面做了许多处理, 这里我只做了get处理,我主要就的目的就是为的是axios是怎样中止允诺的。先看下官方用语:

主要就是两种用语:

采用 cancel token 中止允诺

const

CancelToken = axios.CancelToken;

const

source = CancelToken.source();

axios.get(/user/12345

, {

cancelToken: source.token

}).catch(function

(thrown) {

if

(axios.isCancel(thrown)) {

console.log(Request canceled

, thrown.message);

} else

{

// 处理严重错误

}

});

axios.post(/user/12345

, {

name: new name

}, {

cancelToken: source.token

})

// 中止允诺(message 参数是可选的)source.cancel(Operation canceled by the user.

);

还可以通过传递三个 executor 函数到 CancelToken 的构造函数来创建 cancel token:

const

CancelToken = axios.CancelToken;

let

cancel;

axios.get(/user/12345

, {

cancelToken: new CancelToken(function executor

(c) {

// executor 函数接收三个 cancel 函数做为参数

cancel = c;

})

});

// cancel the request

cancel();

看了官方用语 和结合axios源代码:我给出以下实现:

export class cancelToken

{

constructor

(exactor) {

if (typeof executor !== function

) {

throw new TypeError(executor must be a function.

)

}

// 这里其实将promise的控制权 交给 cancel 函数 // 同时做了防止多次重复cancel 以后 Redux 还有React 源代码中也有类似的案列 const

resolvePromise;

this.promise = new Promise

(resolve => {

resolvePromise = resolve;

})

this.reason = undefined

;

const

cancel = (message) => {

if(this

.reason) {

return

;

}

this.reason = cancel

+ message;

resolvePromise(this

.reason);

}

exactor(cancel)

}

throwIfRequested

() {

if(this

.reason) {

throw this

.reason

}

}

// source 其实本质上是三个句法糖 里面做了封装 static source

() {

const

cancel;

const token = new cancelToken(function executor

(c) {

cancel = c;

});

return

{

token: token,

cancel: cancel

};

}

}

截止到这里大体axios 大体功能已经给出。

接下来我就测试下我的记事本axios 有没甚么问题?

<script type=“module”

>

import Axios from ./axios.js

;

const config = { url:http://101.132.113.6:3030/api/mock

}

const axios = new

Axios();

axios.request(config).then(res => {

console.log(res,0000

)

}).catch(err => {

console

.log(err)

})

打开浏览器看一下结果:

成功了ok, 接着我来测试一下圣夫龙的功能:代码更新成下面这样:

import Axios from ./axios.js

;

const config = { url:http://101.132.113.6:3030/api/mock

}

const axios = new

Axios();

// 在axios 示例上挂载属性const

err = () => {}

axios.interceptors.request.use((config)=> {

console.log(我是允诺圣夫龙1

)

config.id = 1

;

return

config

},err )

axios.interceptors.request.use((config)=> {

config.id =2 console.log(我是允诺圣夫龙2

)

return

config

},err)

axios.interceptors.response.use((data)=> {

console.log(我是积极响应圣夫龙1

,data )

data += 1

;

return

data;

},err)

axios.interceptors.response.use((data)=> {

console.log(我是积极响应圣夫龙2

,data )

return

data

},err)

axios.request(config).then(res => {

// console.log(res,0000) // return res;

}).catch(err => {

console

.log(err)

})

ajax 允诺的结果 我是resolve(1) ,所以他们看下输出路径:

没甚么问题, 积极响应后的数据我加了1。

接下来我来是中止允诺的两种方式

// 第一种方式

let cancelFun = undefined;

const cancelInstance = new cancelToken((c)=>{

cancelFun = c;

});

config.cancelToken = cancelInstance;

// 50 ms 就中止允诺

setTimeout(()=>{

cancelFun(中止成功)

},50)

第二种方式:

const { token, cancel } = cancelToken.source();

config.cancelToken = token;

setTimeout(()=>{

cancel()

},50)

结果都是OK的,至此axios简单源代码终于搞定了。

反思

第一集文章只是把axios源代码的大体业务流程走了一遍, axios源代码内部还是做了许多兼容比如:配置优先级:他有三个mergeConfig 方式, 还有数据转换器。不过那些不影响他们对axios源代码的整体剖析, 源代码中其实有三个createInstance,至于为甚么有?我觉得就是为的是可扩展性更好, 将来有啥新功能,直接在原有axios的示例的原型链上去增加,代码可维护性强, axios.all spread 都是示例new出来再去挂的,不过都很简单,没啥的。有兴趣大家自行阅读。

作者:FE_FLY

juejin.cn/post/6973257605367988260

资源下载此资源下载价格为2.88B,包年VIP免费,请先
2405474279

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务