import { delay } from '../utils/thread' import MD5 from 'crypto-js/md5' import { IRequestOptions } from '../intf/IRequestOptions' import { TOnly } from '../intf/IOnly' /** 缓存 */ const cache: { [hash: string]: Array<{ resolve: Function; reject: Function }> } = {} /** 计算请求hash值 */ const calcHash = (options: IRequestOptions): string => { const { url, params, data, headers } = options return MD5(JSON.stringify({ url, params, data, headers })).toString() } /** 验证重复请求 */ const isRepeat = (hash: string): boolean => { return cache[hash] && cache[hash].length > 0 } /** 处理 only type */ const transformOnlyType = (only: TOnly | undefined): { type: 'merge' | 'error'; delayTime: number } | null => { if (!only) return null if (only === 'merge' || only === 'error') { return { type: only as 'merge' | 'error', delayTime: 100 } } else if (typeof only === 'object') { const { type, delay } = only return { type: transformOnlyType(type)?.type ?? 'merge', delayTime: Number(delay) > 0 ? Number(delay) : 0 } } else { return { type: 'merge', delayTime: 100 } } } /** 处理结果响应 */ const response = (hash: string, success?: any, fail?: any): void => { while (cache[hash].length > 0) { const cb = cache[hash].shift() // 从前向后获取, 避免打乱原有请求顺序 if (success !== undefined) cb?.resolve(success) else if (fail != undefined) cb?.reject(fail) } } /** 处理请求防抖 */ export const only = () => { return function (_target: any, _methodName: any, desc: any) { // 1. 保存当前的方法 const oMethod = desc.value // 2. 改写方法 desc.value = async function (req: IRequestOptions) { const { type, delayTime } = transformOnlyType(req.only) ?? {} const message: string = (req.only as any)?.message ?? '您的操作太快了' const hash: string = calcHash(req) const salf = this // ? 是否存在重复请求 if (isRepeat(hash)) { if (type === 'error') { // @ 抛出异常 throw new Error(message) } else { // > 重复请求写入缓存 return new Promise((resolve, reject) => { cache[hash].push({ resolve, reject }) }) } } else { // 执行 return new Promise(async (resolve, reject) => { if (!cache[hash]) cache[hash] = [] // 缓存callbacK cache[hash].push({ resolve, reject }) // delay await delay(delayTime ?? 0) try { // execute const res = await oMethod.apply(salf, [req]) response(hash, res, undefined) } catch (error) { response(hash, undefined, error) } }) } } } }