-
Notifications
You must be signed in to change notification settings - Fork 374
Expand file tree
/
Copy pathasync_queue.ts
More file actions
76 lines (66 loc) · 2.25 KB
/
Copy pathasync_queue.ts
File metadata and controls
76 lines (66 loc) · 2.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// 定义一个异步任务函数类型,返回 Promise
type TStackFn<T> = (...args: any[]) => Promise<T> | T;
// 链表节点类型,包含任务、Promise 的 resolve/reject、以及下一个节点
type TNode<T> = {
task: TStackFn<T> | null;
resolve: ((v: T | Promise<T>) => void) | null;
reject: ((e?: any) => void) | null;
next: TNode<T> | null;
};
// 队列(栈)结构,包含头部和尾部节点
type TStack<T> = {
head: TNode<T> | null;
tail: TNode<T> | null;
};
// 全局存储不同 key 对应的任务队列
const stacks = {} as Record<string, TStack<any>>;
/**
* 异步执行任务队列中的任务
* 会依次从 head 开始执行,直到队列为空
*/
const startAsync = async <T>(stack: TStack<T>) => {
let node;
while ((node = stack.head)) {
const { task, resolve, reject } = node;
// 目前节点的 task, resolve, reject 已被取出,清理回调引用
node.task = node.resolve = node.reject = null;
try {
// 执行异步任务
const ret = await task!();
resolve!(ret);
} catch (e: any) {
reject!(e);
}
// 移动到下一个节点
stack.head = node.next; // 更新head至下一个节点
// 目前节点的 next 已被取出,清理引用
node.next = null;
}
// 当队列为空时,重置 tail
stack.tail = null;
};
/**
* 向指定 key 的队列中添加异步任务
* 若该队列没有正在执行的任务(!stack.tail),则启动执行
*/
export const stackAsyncTask = <T>(key: string, task: TStackFn<T>): Promise<T> => {
return new Promise((resolve, reject) => {
// 获取或初始化对应 key 的队列
const stack: TStack<T> = stacks[key] || (stacks[key] = { head: null, tail: null });
const newNode: TNode<T> = { task, resolve, reject, next: null };
// 入队逻辑
if (!stack.tail) {
// 队列为空时,设为首尾节点
stack.head = newNode;
stack.tail = newNode;
// ⚠️ 注意:此时会启动任务执行
// 请勿在外部代码中同时多次调用 startAsync
// 当前逻辑依赖 “!stack.tail” 判断来避免并发执行
startAsync<T>(stack);
} else {
// 队列不为空时,追加到尾部
stack.tail.next = newNode;
stack.tail = newNode;
}
});
};