Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
wjgogogo committed Dec 29, 2024
1 parent 169f094 commit b5dc3ad
Show file tree
Hide file tree
Showing 6 changed files with 563 additions and 0 deletions.
89 changes: 89 additions & 0 deletions packages/rspress-site/docs/guide/scheduler.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Scheduler 调度器

Scheduler(调度器)负责管理任务的调度和优先级。定义了任务调度的逻辑,包括任务的优先级、任务队列的管理等。是 React 中非常重要的一个组件。

但 React 并没有将本身的任务优先级调度和 Scheduler 耦合在一起,为了保证其通用性,将 Scheduler 作为一个独立的包存在。我们可以直接安装 [Scheduler](https://www.npmjs.com/package/scheduler) 包来使用。

:::tip
了解事件循环 EventLoop 能更好掌握本章内容,可参考笔者以前写的[消息队列与事件循环](https://mp.weixin.qq.com/s/vzR6Ya4MVQJNijPodhBLww)
:::

## 举一个例子

在深入到源码之前,我们先看看,如果要自己实现一套任务调度机制,应该怎么做。

任务调度器的本质就是将任务按照优先级进行排序,然后按照优先级依次执行任务。

```tsx preview
const taskQueue = [];

let id = 0;
function schedule(execute: () => void, priority: number) {
const task = {
id: id++,
execute,
priority
};
push(task);
}

function perform() {
while (taskQueue.length) {
const task = peek();
task.execute();
}
}

function push(task) {
taskQueue.push(task);
taskQueue.sort((a, b) => a.priority - b.priority);
}

function peek() {
return taskQueue.pop();
}

function longTask(time: number) {
const current = Date.now();
while (Date.now() - current <= time) {}
}

export default () => {
return (
<button
onClick={() => {
schedule(() => console.log("hahah"));
}}
>
123
</button>
);
};

```

![](/d2/mini-scheduler.svg)




## 事件优先级

在 Scheduler 中,任务从高优先级到低优先级分别为:立即执行、用户阻塞级别、普通优先级、低优先
级、闲置。

```ts
export type PriorityLevel = 0 | 1 | 2 | 3 | 4 | 5;
export const NoPriority = 0;
export const ImmediatePriority = 1;
export const UserBlockingPriority = 2;
export const NormalPriority = 3;
export const LowPriority = 4;
export const IdlePriority = 5;
```

## 小顶堆

## 源码解析

![](/d2/scheduler.svg)
44 changes: 44 additions & 0 deletions packages/rspress-site/docs/guide/tests.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const taskQueue = [];

let id = 0;
function schedule(execute: () => void, priority: number) {
const task = {
id: id++,
execute,
priority
};
push(task);
}

function perform() {
while (taskQueue.length) {
const task = peek();
task.execute();
}
}

function push(task) {
taskQueue.push(task);
taskQueue.sort((a, b) => a.priority - b.priority);
}

function peek() {
return taskQueue.pop();
}

function longTask(time: number) {
const current = Date.now();
while (Date.now() - current <= time) {}
}

export default () => {
return (
<button
onClick={() => {
schedule(() => console.log("hahah"));
}}
>
123
</button>
);
};
24 changes: 24 additions & 0 deletions packages/rspress-site/docs/public/d2/mini-scheduler.d2
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
direction: right

taskQueue: {
grid-columns: 3
task1
task2
task3
}

schedule -> taskQueue

perform: {
hasTask: 还存在任务 {
shape: diamond
}

peek: peek\n取出优先级最高的任务

hasTask -> peek: yes
peek -> execute -> hasTask
}

taskQueue -> perform.hasTask
perform.hasTask -> end: no
128 changes: 128 additions & 0 deletions packages/rspress-site/docs/public/d2/mini-scheduler.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
117 changes: 117 additions & 0 deletions packages/rspress-site/docs/public/d2/scheduler.d2
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
direction: down
*.style: {
border-radius: 8
}
Queue: {
TimerQueue: TimerQueue(延迟任务队列)\n优先级按照 startTime 排序 {
timer1 -> timer2 -> timer3
}

TaskQueue: TaskQueue(立即执行任务队列)\n优先级按照 expirationTime 排序 {
task1 -> task2 -> task3
}

shouldDelay: 是否是延迟任务 {
shape: diamond
}
shouldDelay -> TimerQueue: yes

shouldDelay -> TaskQueue: no
}

Schedule: {
isFirstTimer: 当前任务是优先级最高的延迟任务\n且不存在立即执行的任务 {
shape: diamond
width: 360
}

requestHostTimeout: requestHostTimeout\n(使用 timeout 延迟,之后再次检查任务情况)

handleTimeout: {
style: {
stroke-dash: 6
}
advanceTimers: advanceTimers\n(将所有到期的延迟任务移动到TaskQueue中)

hasDelayTask: TaskQueue存在任务 {
shape: diamond
}

advanceTimers -> hasDelayTask
}

schedulePerformWorkUntilDeadline: schedulePerformWorkUntilDeadline\n(通过宏任务延迟调用 performWorkUntilDeadline)

handleTimeout.hasDelayTask -> requestHostTimeout: no

handleTimeout.hasDelayTask -> requestHostCallback: yes

isFirstTimer -> requestHostTimeout: yes

requestHostTimeout -> handleTimeout

requestHostCallback -> schedulePerformWorkUntilDeadline
}

Execution: {
performWorkUntilDeadline -> flushWork -> workLoop.advanceTimers

workLoop: {
style: {
stroke-dash: 6
}
advanceTimers: advanceTimers\n(将所有到期的延迟任务移动到TaskQueue中)

peek: peek\n(从 TaskQueue 中选择优先级最高的任务)

shouldYield: 立即执行任务已经到期\n并且不需要让出主线程给浏览器 {
shape: diamond
width: 400
}

execute: 执行任务

finish: 当前任务执行完成 {
shape: diamond
width: 200
}

advanceTimers -> peek -> shouldYield

shouldYield -> execute: yes

execute -> finish

finish -> advanceTimers: yes
}

hasMore: 存在其他立即执行任务 {
shape: diamond
width: 300
}

hasMoreDelay: 存在其他延迟任务 {
shape: diamond
width: 300
}

workLoop.shouldYield -> hasMore: no

hasMore -> hasMoreDelay: no
}

push: push\n(根据优先级生成任务)

scheduleCallback -> push -> Queue.shouldDelay

Queue.TimerQueue -> Schedule.isFirstTimer

Queue.TaskQueue -> Schedule.requestHostCallback

Schedule.schedulePerformWorkUntilDeadline -> Execution.performWorkUntilDeadline

Execution.workLoop.finish -> Schedule.schedulePerformWorkUntilDeadline: no
Execution.hasMore -> Schedule.schedulePerformWorkUntilDeadline: yes

Execution.hasMoreDelay -> Schedule.requestHostTimeout: yes
Execution.hasMoreDelay -> end: no
Loading

0 comments on commit b5dc3ad

Please sign in to comment.