import { normalizeConfig } from './helpers' import type { AjaxInstance, AjaxOption, AxiosMethods } from './types' import { addCalledCanceller, addCanceller, deleteCancellerByHash } from './canceller' import { generateRequestHash } from './utils' import { addPendingRequest, checkIfHasPendingRequest, clearPendingRequest, getCachedRequest, } from './duplicateRequestPrevent' import { addRequestCanceler, clearCancelerByHash, execCanceler } from './cancelLastSameRequest' // GDataShape通过initAjax的第二个泛型参数传入,单独的请求如果不传返回数据的类型,则默认使用GDataShape // DataType通过ajax.get, ajax.post等方法的泛型参数传入,如果定义了这个参数,会将GDataShape覆盖 export interface RequestCreator { // request = ajax.get(url, options_1) ( url: string, options?: Partial ): { // request(params, options_2) (params?: object | null, opts?: Partial): Promise } } const postLikeMethods = ['PUT', 'POST', 'DELETE', 'PATCH'] export const generateMethod = , GDataShape extends object = any>( ajax: AjaxInstance, method: AxiosMethods ): RequestCreator => /** * * @param url 完整的请求url链接 * @param options ajax配置 * @returns 返回一个发送请求的函数 */ (url: string, options?: Partial) => /** * 发送请求 * @param params get类请求的url参数 or post类请求的请求体数据 * @param opts ajax参数,拥有最高优先级 * @returns 请求数据 */ async (params?: object | null, opts?: Partial) => { const mergedOptions = { ...options, ...opts } const isPostLikeMethod = postLikeMethods.includes(method) const config = { ...mergedOptions, params: isPostLikeMethod ? undefined : params, data: isPostLikeMethod ? params : undefined, url, method, } as any const finalOpts = normalizeConfig(ajax, config) // mock const mock = finalOpts.mock if (mock) { return Promise.resolve(mock) } // 完全相同的请求,且都处于pending状态,复用上一个 const hashKey = generateRequestHash(finalOpts) if (checkIfHasPendingRequest(hashKey)) { // 重复,且处于pending状态的请求,复用 return getCachedRequest(hashKey) } // 这个检查应该位于前面复用请求的逻辑后面,不能乱序 // 如果需要取消之前处于pending状态相同url请求 if (finalOpts.cancelLastSameUrl) { const currUrl = finalOpts.url // 执行所有缓存的url相关的取消函数 execCanceler(currUrl) } // 缓存当前请求的取消函数 if (typeof AbortController !== 'undefined') { // 环境支持AbortController const controller = new AbortController() finalOpts.signal = controller.signal if (typeof mergedOptions.cancelKey !== 'undefined') { addCanceller(mergedOptions.cancelKey, hashKey, () => { controller.abort.call(controller) // 删除挂载在ajax上的请求中断函数 deleteCancellerByHash(mergedOptions.cancelKey as string, hashKey) }) } // 缓存当前请求的取消函数,假如后续有相同的url请求需要取消该请求,可以通过这里缓存的函数实现(只针对get请求) if (finalOpts.cancelLastSameUrl && finalOpts.method.toLocaleLowerCase() === 'get') { addRequestCanceler(finalOpts.url, hashKey, () => { controller.abort.call(controller) clearCancelerByHash(finalOpts.url, hashKey) }) } } else { // 环境如果不支持AbortController,取消函数挂载到ajax只能交给adapter里面去实现了 finalOpts.signal = undefined // 因为异步的关系(axios中发出请求之前,对拦截函数处理可能是异步的),逻辑可能还没走到adapter中去,取消函数就已经被调用了 // 此时取消函数还是undefined,所以这里提供默认取消函数 if (typeof mergedOptions.cancelKey !== 'undefined') { addCanceller(mergedOptions.cancelKey, hashKey, () => { // 默认函数将已经调用的取消函数名称记录下来,在adapter中走到发送请求的逻辑时,直接中断即可 addCalledCanceller(mergedOptions.cancelKey as string, hashKey, true) deleteCancellerByHash(mergedOptions.cancelKey as string, hashKey) }) } } try { const request = ajax.request(finalOpts) addPendingRequest(hashKey, request, finalOpts.reUseDuration) return await request } catch (e) { return Promise.reject(e) } finally { // 请求成功,删除ajax.canceller上对应的取消函数 deleteCancellerByHash(finalOpts.cancelKey, hashKey) // 删除用于取消相同URL请求功能而缓存的取消函数 clearCancelerByHash(finalOpts.url, hashKey) // 删除缓存的请求(用于复用finalOpts.reUseDuration内完全相同的多个请求) clearPendingRequest(hashKey) } }