react学习系列——Scheduler

88 阅读2分钟
import { unstable_scheduleCallback as scheduleCallback } from "./forks/Scheduler.js";
function normalTask(id) {
  return () => {
    console.log('task:', id)
  }
}

scheduleCallback(0, normalTask(1)); // 5000
scheduleCallback(1, normalTask(2)); // -1
scheduleCallback(2, normalTask(3)); // 250
scheduleCallback(3, normalTask(4)); // 5000
scheduleCallback(4, normalTask(5)); // 10000
scheduleCallback(5, normalTask(6)); // Never times out

scheduleCallback第一个参数是优先级,expirationTime = startTime + 注释后的数字

注释后的数字是必须执行时间,即使这帧超时了也要执行

taskQueue按照过期时间排序然后执行,执行完后更新当前时间,所以taskQueue的顺序是1,2,0,3,4,5

在执行taskQueue的过程中,当任务优先级不是1时,就有可能被截断,Scheduler按照5ms为切割点,从任务开始执行到执行完a任务后,时间超过5ms,如果a+1没有超时,这个任务队列在这轮宏任务中就先不执行了,将主线程给其他任务使用

之后在下一次循环中,从a+1这个任务开始继续执行

截屏2024-03-14 17.35.48.png

上图可以看出,轮到task3时间已经不够了,因此task3在下一次宏任务中执行

如果有个很长很长的任务

function longTask(id) { // 预计花费800+ms
  return () => {
    console.log('task:', id)
    let startTime = +new Date()
    for(let i = 0; i < 1000000000; i++) {
      ;
    }
    let lastTime = +new Date() - startTime
    console.log("longTask spend:", lastTime)
  }
}

scheduleCallback(2, longTask(7));

截屏2024-03-14 17.39.44.png

如上图所示,整个taskQueue被截成3段,2 | 3,7 | 1,4,5,6

当task3执行完后,时间还很充裕,就执行task7这个长任务,等它执行完后,这帧花费的时间已经800多ms了,之后执行task1,由于task1超时时间是5000+ms,没有超时,就从这里截断。

如果有个延迟任务


scheduleCallback(2, normalTask(8), {
  delay: 500
});

task8的过期时间是500+250 = 750

那么此时task7执行完后,由于task8超时了,所以在执行完task8后再截断

截屏2024-03-14 18.18.02.png

如果task返回是一个函数

const result = 3;
let currentResult = 0;
function calculate() {
  console.log('calculate:', currentResult);
  currentResult++;
  if (currentResult < result) {
    return calculate;
  }
  return null;
}

calculate()会调用自己三次

此时Scheduler会判断当callback是函数的时候,这个此时循环会直接停止,并把返回的callback赋值给当前task,等待下一次宏任务继续从这个task开始执行

截屏2024-03-14 19.45.15.png