-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
563 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
128
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.