返回

时间切片

// src/scheduler.js 

// 流程:scheduleCallback => planWork => flushWork => flush => planWork=>...

import { push, pop, peek } from "./heapify";
// 任务队列
let taskQueue = [];
// 当前回调函数
let currentCallback = null;
let frameDeadline = 0;
// 切片时间长度
const frameLength = 1000 / 60;

export function scheduleCallback(callback) {
  // 获取更精确的微妙级别的时间
  const currentTime = performance.now();
  const startTime = currentTime;
  const timeout = 3000;
  // 过期时间
  const dueTime = startTime + timeout;

  let newTask = {
    callback,
    startTime,
    dueTime
  };
  // 装入任务队列
  push(taskQueue, newTask);
  currentCallback = flush;
  planWork();
}
// 观察任务执行情况
function flush(iniTime) {
  let currentTime = iniTime;
  let currentTask = peek(taskQueue);

  while (currentTask) {
    // 如果任务过期并且切片时间到了跳出循环
    if (
      currentTask.dueTime > currentTime &&
      performance.now() >= frameDeadline
    ) {
      break;
    }
    let callback = currentTask.callback;
    currentTask.callback = null;
    // 完成了没有
    const didout = currentTask.dueTime <= currentTime;

    let next = callback(didout);
    // 没完成继续完成了移除任务
    next ? (currentTask.callback = next) : pop(taskQueue);

    //  接着执行任务或者开启下一个任务
    currentTask = peek(taskQueue);
    currentTime = performance.now();
  }

  return !!currentTask;
}

function flushWork() {
  if (currentCallback) {
    let currentTime = performance.now();
    frameDeadline = currentTime + frameLength;
    let more = flush(currentTime);
    more ? planWork() : (currentCallback = null);
  }
}

// 使用消息轨道还是定时器处理任务
export const planWork = (() => {
  if (typeof MessageChannel !== "undefined") {
    const { port1, port2 } = new MessageChannel();
    port1.onmessage = flushWork;
    return () => port2.postMessage(null);
  }
  return () => setTimeout(flushWork);
})();