我在阅读16.8源码中遇到个问题,没有思路,不知道各位能不能提供一下想法?
假设有 一个NormalPriority的更新A 和 一个UserBlockingPriority的更新B
A先进入调度,Scheduler 的taskQueue 中有就存在了一个taske { callback: A, id: A }
A在render过程中,来了B,这时候按照源码会cancelTask,同时Scheduler开始调度B,这个过程结束后 taskQueue [{ callback: null, id: A }, { callback: B, id: B}]
然后我想知道
- taskQueue中ATask的callback为null后是否会被清除?即taskQueue是否有一个状态是[{ callback: B, id: B}]? 没有ATask和ATask.callback = null我的理解是这两种情况都会中断A的更新,我的理解是否正确?
2.在B更新渲染完成后是如何重新开始调度A的?
- 在重新调度A的时候是否会将task塞入taskQueue,即taskQueue在taskB完成并被剔除后,是否有一时刻状态为[{ callback: A, id: A }]; 如果有这个状态,那么这个ATask是什么时候被塞入的?
问题1 taskQueue中ATask的callback为null后是否会被清除?
不会被清除,这是代码实现的问题,要去删除一个优先队列里面一个指定的元素是一件吃力不讨好的事情,如果要删除就不得不去遍历整个队列找到该元素然后在把该元素和末尾元素swap然后在对该元素所在位置的元素进行一下siftUp和siftDown(具体原理可以搜索优先队列删除元素)时间复杂度为O(N)其中N为队列长度,不如直接把该Task的callback标记为null(表示一个不需要进行任何操作的任务),等到执行该任务的时候,发现他的callback为null直接把它出队就行具体的逻辑可以看这里
问题2 在高优先级任务被调度后,taskQueue是否有一个状态是[{ callback: B, id: B}]?
根据第一个问题的答案,一个任务会被从队列里删除,只存在一种情况那就是他对应的操作执行完了
所以秉着高优先级的任务一定比低优先级任务先执行的原则在B被出队之前A会一直呆在队列里面,只是他被他已经被标识为一个不需要进行任何操作的任务
问题3 没有ATask和ATask.callback = null我的理解是这两种情况都会中断A的更新,我的理解是否正确?
算是正确吧,如果把React比作一个执行渲染任务的系统,他就一直在循环往复的做三件事情
(1)从优先队列中挑选优先级最高的渲染任务
(2)执行该渲染任务
(3)把执行完的任务弹出队列
其中在执行渲染任务的过程中,还得响应中断(中断的类型可以是浏览器的交互,此时的控制权就交给了浏览器,等到一定时机React又会继续渲染任务,有点反客为主了哈哈,而在交互的过程中又有可能产生新的优先级更高的渲染任务比如你题目中的渲染任务B),如果在响应中断的过程用中产生了新的高优先级任务,在恢复渲染时会先取出执行这个高优先级的任务,而做到途中但是还未完成的A就被中断了(与其说是中断了A不如说取消了A,因为A待会会被重新入队重新开始执行),当然一个任务不可能被一直被中断,每个任务都有一个超时时间,如果发现一个任务超时以后React会同步的执行完该任务,期间不会响应任何中断,上面的流程都发生在Scheduler模块的workLoop函数中
问题4 在重新调度A的时候是否会将task塞入taskQueue,即taskQueue在taskB完成并被剔除后,是否有一时刻状态为[{ callback: A, id: A }],如果有这个状态,那么这个ATask是什么时候被塞入的?
这个问题在问题3里已经解答了,A被取消后,是会被重新入队的,以你的例子为例在完成了B任务后,React会调用
ensureRootIsScheduled
把下一个优先级最高的任务入队,具体代码看这里