异步队列模块
需求背景
- 多个模块之间,需要调用相同的异步请求,为了减少重复请求,使用队列来缓存相同的请求
设计思路
通过内部维护一个队列 Map,不同的 key 对应的不同的请求,同时对外暴露相关方法来操作这个 Map:
export default class AsyncQueue {
private asyncSet: Map<string, Array<Promise<any>>>;
constructor() {
this.asyncSet = new Map<string, Array<Promise<any>>>();
}
/**
* 缓存指定 key 的异步请求
*/
public setPromise(key: string, anyPromise: Promise<any>): void {
const cur = this.asyncSet.get(key) || [];
if (!cur) {
// 初始化 key: []
this.asyncSet.set(key, []);
}
cur.push(anyPromise);
this.asyncSet.set(key, cur);
}
/**
* 获取指定 key 的异步请求
*/
public getPromise(key: string): Promise<any> | undefined {
let result;
const queue = this.asyncSet.get(key);
if (queue && queue.length) {
result = queue.pop();
}
return result;
}
/**
* cachePromise 缓存请求
* @param key 唯一索引 Key
* @param fn 异步请求
*/
public cachePromise<T>(key: string, fn: () => Promise<T>): () => Promise<T> {
let cachePromise = this.getPromise<T>(key);
if (!cachePromise) {
cachePromise = fn();
this.setPromise(key, cachePromise);
}
// 使用 ! 非空断言
return () => cachePromise!;
}
....
}
建议项目内部维护唯一单例
使用方式
小程序
建议在 app.ts
中的 globalData
里面,初始化队列:
import AsyncQueue from '@retailwe/common-libs-async-queue';
App({
globalData: {
asyncQueue: new AsyncQueue()
}
});
业务层请求缓存封装
// service 使用
const mockService = async (key: string): Promise<any> => {
const testPromise = queue.getPromise(key);
if (testPromise) return testPromise;
const tempPromise = asyncFn(key);
queue.setPromise(key, tempPromise);
const val = await tempPromise;
return new Promise(resolve => resolve(val));
};
// 单元测试
test('asnycQueue test1', async t => {
const key1 = 'asyncQueue1';
const promiseAll = await Promise.all([mockService(key1), mockService(key1)]);
t.is(queue.getQueue(key1)?.length, 0);
t.is(JSON.stringify(promiseAll), JSON.stringify([1, 1]));
});
// 通过 cachePromise 直接缓存一个 () => Promise<any> 函数
test.serial('test cachePromise', async t => {
const key = 'cachePromise';
const fn1 = queue.cachePromise<number>(key, () => mockPromiseFn(1));
t.is(queue.getQueue(key)?.length, 1);
t.is(await fn1(), 1);
const fn2 = queue.cachePromise<number>(key, () => mockPromiseFn(2));
// 这里会使用上一次队列的缓存,所以第二次的结果是上一次的结果
t.is(await fn2(), 1);
// cache 相同 key, 队列数不变,
t.is(queue.getQueue(key)?.length, 0);
// 队列 map.size + 1
t.is(queue.getQueues().size, 3);
});