网络编程
位置:首页>> 网络编程>> JavaScript>> axios  * 管理类链式调用手写实现及原理剖析

axios  * 管理类链式调用手写实现及原理剖析

作者:小p  发布时间:2023-07-02 16:38:23 

标签:axios, , ,管理类,链式调用

axios库的 * 使用

我们知道axios库的 * 的使用方式如下:

// 添加一个请求 *
axios.interceptors.request.use(function (config) {
 // 在发送请求之前可以做一些事情
 return config;
}, function (error) {
 // 处理请求错误
 return Promise.reject(error);
});
// 添加一个响应 *
axios.interceptors.response.use(function (response) {
 // 处理响应数据
 return response;
}, function (error) {
 // 处理响应错误
 return Promise.reject(error);
});

在 axios 对象上有一个 interceptors 对象属性,该属性又有 request 和 response 2 个属性,它们都有一个 use 方法,use 方法支持 2 个参数,第一个参数类似 Promise.then 的 resolve 函数,第二个参数类似 Promise.then 的 reject 函数。我们可以在 resolve 函数和 reject 函数中执行同步代码或者是异步代码逻辑。

并且我们是可以添加多个 * 的, * 的执行顺序是链式依次执行的方式。对于 request  * ,后添加的 * 会在请求前的过程中先执行;对于 response  * ,先添加的 * 会在响应后先执行。

axios.interceptors.request.use(config => {
 config.headers.test += '1'
 return config
})
axios.interceptors.request.use(config => {
 config.headers.test += '2'
 return config
})

此外,我们也可以支持删除某个 * ,如下:

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

整体设计

我们先用一张图来展示一下 * 工作流程:

axios  * 管理类链式调用手写实现及原理剖析

整个过程是一个链式调用的方式,并且每个 * 都可以支持同步和异步处理,我们自然而然地就联想到使用 Promise 链的方式来实现整个调用过程。

在这个 Promise 链的执行过程中,请求 *  resolve 函数处理的是 config 对象,而相应 *  resolve 函数处理的是 response 对象。

在了解了 * 工作流程后,我们先要创建一个 * 管理类,允许我们去添加 删除和遍历 * 。

* 管理类实现

根据需求,axios 拥有一个 interceptors 对象属性,该属性又有 request 和 response 2 个属性,它们对外提供一个 use 方法来添加 * ,我们可以把这俩属性看做是一个 * 管理对象。

use 方法支持 2 个参数,第一个是 resolve 函数,第二个是 reject 函数,对于 resolve 函数的参数,请求 * 是 AxiosRequestConfig 类型的,而响应 * 是 AxiosResponse 类型的;而对于 reject 函数的参数类型则是 any 类型的。

根据上述分析,我们先来定义一下 * 管理对象对外的接口。

接口定义

这里我们定义了 AxiosInterceptorManager 泛型接口,因为对于 resolve 函数的参数,请求 * 和响应 * 是不同的。

export interface AxiosInterceptorManager<T> {
 use(resolved: ResolvedFn<T>, rejected?: RejectedFn): number
 eject(id: number): void
}
export interface ResolvedFn<T=any> {
 (val: T): T | Promise<T>
}
export interface RejectedFn {
 (error: any): any
}

代码实现

import { ResolvedFn, RejectedFn } from '../types'
interface Interceptor<T> {
 resolved: ResolvedFn<T>
 rejected?: RejectedFn
}
export default class InterceptorManager<T> {
 private interceptors: Array<Interceptor<T> | null>
 constructor() {
   // * 数组
   this.interceptors = []
 }
 // 收集 *  
 use(resolved: ResolvedFn<T>, rejected?: RejectedFn): number {
   this.interceptors.push({
     resolved,
     rejected
   })
   return this.interceptors.length - 1
 }
 // 遍历用户写的 * ,并执行fn函数把 * 作为参数传入
 forEach(fn: (interceptor: Interceptor<T>) => void): void {
   this.interceptors.forEach(interceptor => {
     if (interceptor !== null) {
       fn(interceptor)
     }
   })
 }
 eject(id: number): void {
   if (this.interceptors[id]) {
     // 置为null,不能直接删除
     this.interceptors[id] = null
   }
 }
}

我们定义了一个 InterceptorManager 泛型类,内部维护了一个私有属性 interceptors,它是一个数组,用来存储 * 。该类还对外提供了 3 个方法,其中 use 接口就是添加 * 到 interceptors 中,并返回一个 id 用于删除;

forEach 接口就是遍历 interceptors 用的,它支持传入一个函数,遍历过程中会调用该函数,并把每一个 interceptor 作为该函数的参数传入;eject 就是删除一个 * ,通过传入 * 的 id 删除。

链式调用实现

当我们实现好 * 管理类,接下来就是在 Axios 中定义一个 interceptors 属性,它的类型如下:

interface Interceptors {
 request: InterceptorManager<AxiosRequestConfig>
 response: InterceptorManager<AxiosResponse>
}
export default class Axios {
 interceptors: Interceptors
 constructor() {
   this.interceptors = {
     request: new InterceptorManager<AxiosRequestConfig>(),
     response: new InterceptorManager<AxiosResponse>()
   }
 }
}

Interceptors 类型拥有 2 个属性,一个请求 * 管理类实例,一个是响应 * 管理类实例。我们在实例化 Axios 类的时候,在它的构造器去初始化这个 interceptors 实例属性。

接下来,我们修改 request 方法的逻辑,添加 * 链式调用的逻辑:

interface PromiseChain {
 resolved: ResolvedFn | ((config: AxiosRequestConfig) => AxiosPromise)
 rejected?: RejectedFn
}
request(url: any, config?: any): AxiosPromise {
 if (typeof url === 'string') {
   if (!config) {
     config = {}
   }
   config.url = url
 } else {
   config = url
 }
 // 定义一个数组,这个数组就是要执行的任务链,默认有一个真正发送请求的任务
 const chain: PromiseChain[] = [{
   resolved: dispatchRequest,
   rejected: undefined
 }]
 // 把用户定义的请求 * 存放到任务链中,请求 * 最后注册的最先执行,所以使用unshift方法
 this.interceptors.request.forEach(interceptor => {
   chain.unshift(interceptor)
 })
 // 把响应 * 存放到任务链中
 this.interceptors.response.forEach(interceptor => {
   chain.push(interceptor)
 })
 // 利用config初始化一个promise
 let promise = Promise.resolve(config)
 // 遍历任务链
 while (chain.length) {
   // 取出任务链的首个任务
   const { resolved, rejected } = chain.shift()!
   // resolved的执行时机是就是上一个promise执行resolve()的时候,这样就形成了链式调用
   promise = promise.then(resolved, rejected)
 }
 return promise
}

首先,构造一个 PromiseChain 类型的数组 chain,并把 dispatchRequest 函数赋值给 resolved 属性;接着先遍历请求 * 插入到 chain 的前面;然后再遍历响应 * 插入到 chain 后面。

接下来定义一个已经 resolve 的 promise,循环这个 chain,拿到每个 * 对象,把它们的 resolved 函数和 rejected 函数添加到 promise.then 的参数中,这样就相当于通过 Promise 的链式调用方式,实现了 * 一层层的链式调用的效果。

注意我们 * 的执行顺序,对于请求 * ,先执行后添加的,再执行先添加的;而对于响应 * ,先执行先添加的,后执行后添加的。

来源:https://juejin.cn/post/7136065256941420552

0
投稿

猜你喜欢

手机版 网络编程 asp之家 www.aspxhome.com