美文网首页react
react浅析3 任务调度

react浅析3 任务调度

作者: 百里哈哈 | 来源:发表于2020-09-09 13:40 被阅读0次

可以做一个简单的示例其代码如下

import Scheduler from 'scheduler'
let NormalPriority = Scheduler.unstable_NormalPriority;
let ImmediatePriority = Scheduler.unstable_ImmediatePriority;
let LowPriority = Scheduler.unstable_LowPriority;
let scheduleCallback = Scheduler.unstable_scheduleCallback;

function test1() {
    scheduleCallback(NormalPriority, () => {
        console.log('A')
    })
    scheduleCallback(ImmediatePriority, () => {
        console.log('B')
    })
    scheduleCallback(LowPriority, () => {
        console.log('C')
    })
    scheduleCallback(NormalPriority, () => {
        console.log('D')
    })
    // console.log('out log tag')
}
test1();

通过scheduleCallback放入需要调度的任务, 我们对任务的优先级分别进行NormalPriority、ImmediatePriority、NormalPriority
可以看到其输出结果为BADC

Scheduler.png

unstable_scheduleCallback

  1. 创建一个task任务, 包括计算设置它的截止时间(expirationTime)
  2. 如果当前任务是一个延时任务,
    a. 将新任务放入timerQueue(延时队列)中
    b. 正在执行的任务队列为空且该任务为待执行中最早的,则延时requestHostCallback的调用
  3. 如果当前任务是非延时异步任务,则将其放入taskQueue,调用requestHostCallback方法
function unstable_scheduleCallback(priorityLevel, callback, options) {
  var currentTime = exports.unstable_now();
  var startTime;
  var timeout;

  if (typeof options === 'object' && options !== null) {
    var delay = options.delay;

    if (typeof delay === 'number' && delay > 0) {
      startTime = currentTime + delay;
    } else {
      startTime = currentTime;
    }

    timeout = typeof options.timeout === 'number' ? options.timeout : timeoutForPriorityLevel(priorityLevel);
  } else {
    timeout = timeoutForPriorityLevel(priorityLevel);
    startTime = currentTime;
  }

  var expirationTime = startTime + timeout;
  var newTask = {
    id: taskIdCounter++,
    callback: callback,
    priorityLevel: priorityLevel,
    startTime: startTime,
    expirationTime: expirationTime,
    sortIndex: -1
  };

  {
    newTask.isQueued = false;
  }

  if (startTime > currentTime) {
    // This is a delayed task.
    newTask.sortIndex = startTime;
    push(timerQueue, newTask);

    if (peek(taskQueue) === null && newTask === peek(timerQueue)) {
      // All tasks are delayed, and this is the task with the earliest delay.
      if (isHostTimeoutScheduled) {
        // Cancel an existing timeout.
        cancelHostTimeout();
      } else {
        isHostTimeoutScheduled = true;
      } // Schedule a timeout.


      requestHostTimeout(handleTimeout, startTime - currentTime);
    }
  } else {
    newTask.sortIndex = expirationTime;
    push(taskQueue, newTask);

    {
      markTaskStart(newTask, currentTime);
      newTask.isQueued = true;
    } // Schedule a host callback, if needed. If we're already performing work,
    // wait until the next time we yield.


    if (!isHostCallbackScheduled && !isPerformingWork) {
      isHostCallbackScheduled = true;
      requestHostCallback(flushWork);
    }
  }

  return newTask;
}
 requestHostTimeout = function (cb, ms) {
    _timeoutID = setTimeout(cb, ms);
  };

requestHostCallback

如果是支持MessageChannel,requestHostCallback则采用postMessage异步进行实现。
不支持的使用setTimout来实现。

var channel = new MessageChannel();
var port = channel.port2;
channel.port1.onmessage = performWorkUntilDeadline;
requestHostCallback = function (callback) {
    scheduledHostCallback = callback;

    if (!isMessageLoopRunning) {
      isMessageLoopRunning = true;
      port.postMessage(null);
    }
  };

补充一个浏览器异步事件测试用例, 其结果为 MutationObserver Promise postMessage setTimeout.
如果MutationObserver与Promise测试调换的话其结果为 Promise MutationObserver postMessage setTimeout, 说明二者优先级相同。

function test2() {
  setTimeout(function() {
    console.log('setTimeout')
  }, 0)
  const channel = new MessageChannel();
  const port = channel.port2;
  channel.port1.onmessage = function () {
      console.log('postMessage')
  };
  port.postMessage(null);

  const targetNode = document.createElement('div');
  const config = { attributes: true, childList: true, subtree: true };
  const callback = function () {
      console.log('MutationObserver')
  };
  const observer = new MutationObserver(callback);
  observer.observe(targetNode, config);
  targetNode.setAttribute('id', 'item');

  const promise = new Promise(function (resovle, reject) {
    resovle('Promise');
  })
  promise.then(function(val) {
    console.log(val)
  }, function() {});
}

performWorkUntilDeadline

有代码可知, 如果scheduledHostCallback(requestHostCallback方法中,会将它赋值为flushWork)存在时,则调用该方法。
a. 如果在浏览器的空余时间片结束之后, 还有任务需要执行则通过postMessage推入到异步中等待下一次空余时间执行
b. 如果在执行中捕获到错误,依旧通过postMessage推入到异步中等待下一次空余时间执行。

var performWorkUntilDeadline = function () {
    if (scheduledHostCallback !== null) {
      var currentTime = exports.unstable_now(); // Yield after `yieldInterval` ms, regardless of where we are in the vsync
      // cycle. This means there's always time remaining at the beginning of
      // the message event.

      deadline = currentTime + yieldInterval;
      var hasTimeRemaining = true;

      try {
        var hasMoreWork = scheduledHostCallback(hasTimeRemaining, currentTime);

        if (!hasMoreWork) {
          isMessageLoopRunning = false;
          scheduledHostCallback = null;
        } else {
          // If there's more work, schedule the next message event at the end
          // of the preceding one.
          port.postMessage(null);
        }
      } catch (error) {
        // If a scheduler task throws, exit the current browser task so the
        // error can be observed.
        port.postMessage(null);
        throw error;
      }
    } else {
      isMessageLoopRunning = false;
    } // Yielding to the browser will give it a chance to paint, so we can
  };

flushWork

该方法主要是对workLoop进行调用

function flushWork(hasTimeRemaining, initialTime) {
  isHostCallbackScheduled = false;

  if (isHostTimeoutScheduled) {
    // We scheduled a timeout but it's no longer needed. Cancel it.
    isHostTimeoutScheduled = false;
    cancelHostTimeout();
  }

  isPerformingWork = true;
  var previousPriorityLevel = currentPriorityLevel;

  try {
    if (enableProfiling) {
      try {
        return workLoop(hasTimeRemaining, initialTime);
      } catch (error) {
        if (currentTask !== null) {
          var currentTime = exports.unstable_now();
          markTaskErrored(currentTask, currentTime);
          currentTask.isQueued = false;
        }

        throw error;
      }
    } else {
      // No catch in prod codepath.
      return workLoop(hasTimeRemaining, initialTime);
    }
  } finally {
    currentTask = null;
    currentPriorityLevel = previousPriorityLevel;
    isPerformingWork = false;

    {
      var _currentTime = exports.unstable_now();

      markSchedulerSuspended(_currentTime);
    }
  }
}

workLoop

有代码可知,通过该方法执行task任务,其返回值为是否还有待执行的task

function workLoop(hasTimeRemaining, initialTime) {
  var currentTime = initialTime;
  advanceTimers(currentTime);
  currentTask = peek(taskQueue);

  while (currentTask !== null && !(enableSchedulerDebugging )) {
    if (currentTask.expirationTime > currentTime && (!hasTimeRemaining || shouldYieldToHost())) {
      // This currentTask hasn't expired, and we've reached the deadline.
      break;
    }

    var callback = currentTask.callback;

    if (callback !== null) {
      currentTask.callback = null;
      currentPriorityLevel = currentTask.priorityLevel;
      var didUserCallbackTimeout = currentTask.expirationTime <= currentTime;
      markTaskRun(currentTask, currentTime);
      var continuationCallback = callback(didUserCallbackTimeout);
      currentTime = exports.unstable_now();

      if (typeof continuationCallback === 'function') {
        currentTask.callback = continuationCallback;
        markTaskYield(currentTask, currentTime);
      } else {
        {
          markTaskCompleted(currentTask, currentTime);
          currentTask.isQueued = false;
        }

        if (currentTask === peek(taskQueue)) {
          pop(taskQueue);
        }
      }

      advanceTimers(currentTime);
    } else {
      pop(taskQueue);
    }

    currentTask = peek(taskQueue);
  } // Return whether there's additional work


  if (currentTask !== null) {
    return true;
  } else {
    var firstTimer = peek(timerQueue);

    if (firstTimer !== null) {
      requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);
    }

    return false;
  }
}

react-dom中进入任务调度的入口方法 scheduleCallback

function scheduleCallback(reactPriorityLevel, callback, options) {
  var priorityLevel = reactPriorityToSchedulerPriority(reactPriorityLevel);
  return Scheduler_scheduleCallback(priorityLevel, callback, options);
}

unstable_cancelCallback

unstable_cancelCallback进行任务取消, 它并不会进行删除而是将isQueued值为false

function unstable_cancelCallback(task) {
  {
    if (task.isQueued) {
      var currentTime = exports.unstable_now();
      markTaskCanceled(task, currentTime);
      task.isQueued = false;
    }
  } // Null out the callback to indicate the task has been canceled. (Can't
  // remove from the queue because you can't remove arbitrary nodes from an
  // array based heap, only the first one.)

相关文章

  • react浅析3 任务调度

    可以做一个简单的示例其代码如下 通过scheduleCallback放入需要调度的任务, 我们对任务的优先级分别进...

  • scheduler模块 - React源码解析

    简述 React中的scheduler模块,主要用于React Fiber框架,为React提供任务调度功能。目前...

  • Ooize任务调度框架浅析

    为什么需要任务调度框架 在进行数据处理的时候,需要进行数据采集、数据清洗、数据分析等操作,每一个过程都可能涉及到多...

  • react17源码解读-设计理念

    react架构主要分为两个阶段 render 阶段包括调度和协调调度器 scheduler:调度任务的优先级,高优...

  • react源码学习(二)scheduleWork任务调度

    任务调度 在上一篇中说过了render的过程,最后会调用scheduleWork来执行任务。react将不同的任务...

  • 模仿实现react fiber 任务调度

    在你已经知道什么是fiber以及react为什么需要fiber,并且知道raf和ric这两个api的前提下阅读 r...

  • linux crontab学习总结

    linux crontab用法学习 crond 简介 1.系统任务调度 2. 用户任务调度 3.crond服务 4...

  • Okhttp主流程源码浅析(2)

    上一篇Okhttp主流程源码浅析(1)分析到任务调度方面,接着把剩下的主流程分析. 当一个任务被执行起来,会调用g...

  • linux定时任务

    一 、简介 Linux下的任务调度分为两类,系统任务调度和用户任务调度 系统任务调度:系统需要定期执行的任务,比如...

  • Linux 系统之crontab命令

    Linux下的任务调度分为两类,系统任务调度 和 用户任务调度。 系统任务调度:系统周期性所要执行的工作,比如写缓...

网友评论

    本文标题:react浅析3 任务调度

    本文链接:https://www.haomeiwen.com/subject/kskdektx.html