# 为什么需要 React Scheduler 调度
如果组件 Render 或者组件树节点非常多的情况下,一次 loop 处理所有节点会非常耗时,并且占用主线程导致页面无法交互
React Scheduler 可以结合 React Fiber 进行任务的调用,使得能够独立执行每个 VDOM 的更新,并且能够中断和恢复。
# React Scheduler 调度过程
在了解 MessageChannel 之前,我们先结合源码了解一下 React 调度过程
在源码中,schedulePerformWorkUntilDeadline 就是调度一个 messageChannel,在 messageChannel 的回调中执行 performWorkUntilDeadline 函数
1 | if (typeof MessageChannel !== 'undefined') { |
请注意 scheduledHostCallback 实际就是 flushWork 函数
1 | function requestHostCallback(callback) {// 过期任务请求调度 |
这是调度逻辑
1 | const performWorkUntilDeadline = () => { // 调度时候执行的函数 |
其余部分不重要就不附带源码了,稍微总结一下过程:
- React Scheduler 通过 MessageChannel 调度 performWorkUntilDeadline 执行 Task
- performWorkUntilDeadline 执行 flushWork, flushWork 实际执行 workLoop
- flushWork 返回值为是否还有任务
- performWorkUntilDeadline 会根据 flushWork 的返回值决定是否继续调度(schedulePerformWorkUntilDeadline)
# Scheduler 与 MessageChannel
为什么 Scheduler 需要使用 MessageChannel?
Scheduler 需要满足
- 能够暂停 JS 执行,主线程返回给浏览器
- 能够在未来重新调度该任务
# 宏任务
事件循环 loop 每次执行一个宏任务。
本轮注册的宏任务会在下轮事件循环中执行,不会阻塞本次更新
# 微任务
每次执行宏任务完都会检查是否还有新的微任务,执行,直到没有新的微任务
本轮注册的微任务会在本轮事件循环中执行,会阻塞本次更新
所以?我们需要:能够注册宏任务的 API。
MessageChannel 的目的就是为了产生宏任务
# 为什么不使用 setTimeout (callback, 0) 产生宏任务?
setTimeout (callback, 0) 会产生宏任务,但如果 setTimeout递归层数太深
会有 4ms 的限制
# 总结
Scheduler 需要满足
- 能够暂停 JS 执行,主线程返回给浏览器
- 能够在未来重新调度该任务
微任务为什么不行
- 微任务会在本轮事件循环全部执行完毕,达不到将主线程还给浏览器
setTimeout 为什么不行
- 递归太深会有 4ms 限制