Взгляд на исходный код реакции (3): больше не таинственный механизм приоритета

React.js
Взгляд на исходный код реакции (3): больше не таинственный механизм приоритета

Зачем нужен приоритет

Конечной целью механизма приоритета является достижениеЗадачи с высоким приоритетом выполняются первыми, а задачи с низким приоритетом выполняются позже..

Суть этого назначения в том, что при выполнении низкоприоритетной задачи, если приходит более высокоприоритетная задача, выполнение низкоприоритетной задачи может быть прервано.

Среда выполнения React в синхронном режиме

Мы знаем, что в синхронном режиме изsetStateОт обхода виртуального DOM до реального обновления DOM весь процесс выполняется синхронно и не может быть прерван, поэтому может возникнуть проблема — обновление, вызванное пользовательскими событиями, заблокировано.

Что такое заблокированное обновление, вызванное пользовательским событием? еслиReactВыполняется задача обновления, и пользователь инициирует событие взаимодействия, которое выполняется в обратном вызове события.setState,существуетРежим синхронизацииДалее для этой задачи обновления требуется等待Он будет выполнен только после завершения текущей обновляемой задачи. Если текущийReactТекущая задача обновления занимает много времени, и задача обновления, запущенная пользовательским событием, не может быть выполнена вовремя, что приводит к блокировке следующей задачи обновления, что приводит к зависанию.

В настоящее время мы надеемся, что сможем своевременно реагировать на события, инициированные пользователем, и расставлять приоритеты при выполнении задач обновления, инициируемых пользовательскими событиями, что мы называемАсинхронный режим

Мы можем сравнить,Режим синхронизацииСяхэАсинхронный режим(优先级机制), чтобы обновить разницу в выполнении задачи

import React from "react";
import "./styles.css";

export default class extends React.Component {
  constructor() {
    super();
    this.state = {
      list: new Array(10000).fill(1),
    };
    this.domRef = null;
  }
  componentDidMount() {
    setTimeout(() => {
      console.log("setTimeout 准备更新", performance.now());
      this.setState(
        {
          list: new Array(10000).fill(Math.random() * 10000),
          updateLanes: 16
        },
        () => {
          console.log("setTimeout 更新完毕", performance.now());
        }
      );
    }, 100);
    setTimeout(() => {
      this.domRef.click();
    }, 150);
  }

  render() {
    const { list } = this.state;
    return (
      <div
        ref={(v) => (this.domRef = v)}
        className="App"
        onClick={() => {
          console.log("click 准备更新", performance.now());
          this.setState(
            { list: new Array(10000).fill(2), updateLanes: 1 },
            () => {
              console.log("click 更新完毕", performance.now());
            }
          );
        }}
      >
        {list.map((i, index) => (
          <h1 key={i + +index}>Hello {i}</h1>
        ))}
      </div>
    );
  }
}

Приведенный выше код находится вРежим синхронизацииПечать ниже

同步更新.png

можно увидетьclick事件Запущено обновление, нужно подождатьsetTimeoutИнициированные обновления будут выполняться

Приведенный выше код находится вАсинхронный режимПечать ниже

高优先级任务打断低优先级任务.png

можно увидетьclick事件Триггерных обновлений будет меньше, чемsetTimeoutТриггерные обновления выполняются более предпочтительно, чтобы своевременно реагировать на пользовательские события и прерыватьsetTimeoutобновить задачу (低优先级任务) исполнение.

Я в приведенном выше кодеcodesandboxНаписал демо и запустил их отдельноreact15а такжеreact18Под двумя версиями, если интересно, можно поковыряться напрямую.

Как использовать механизм приоритета для оптимизации времени выполнения реакции

Чтобы устранить дефекты рендеринга в синхронном режиме, мы надеемся, что сможемreactСделайте следующие оптимизации

  1. Приоритизируйте обновления, вызванные различными сценариями, чтобы мы могли решить, каким задачам отдать приоритет.
  2. Если приходит задача с более высоким приоритетом, нам нужно прервать текущую задачу и выполнить эту задачу с высоким приоритетом
  3. Убедитесь, что задачи с низким приоритетом не прерываются все время и могут быть обновлены до задачи с наивысшим приоритетом через определенный период времени.

Определение приоритетов планирования в различных сценариях

ВидимыйreactУ мелких партнеров исходного кода может возникнуть сомнение, почему в исходном коде так много слов, связанных с приоритетом? ? Как их отличить?

по фактуreactСуществуют в основном два типа приоритетов,schedulerприоритет иlaneприоритет,laneНиже приоритет выводится сноваeventприоритет

  • Приоритет полосы: он в основном используется для проверки приоритета текущей задачи и запланированной задачи перед планированием задачи, чтобы определить, нужно ли прервать текущую текущую задачу.
  • Приоритет события: это, по сути, также приоритет полосы, приоритет полосы является общим, приоритет события больше сочетается с собственными событиями браузера, а приоритет полосы классифицируется и отображается.
  • приоритет планировщика: в основном используется для расчета времени истечения задачи при квантовании времени

приоритет полосы движения

Понятие дорожки можно использовать для понимания приоритета дорожки. Существует 31 приоритет дорожки. Мы можем использовать 31-битное двоичное значение для его представления. Каждый бит значения представляет дорожку, соответствующую приоритету дорожки.Чем выше позиция дорожки, тем выше приоритет

приоритет десятичное значение двоичное значение отслеживать местоположение
NoLane 0 0000000000000000000000000000000 0
SyncLane 1 0000000000000000000000000000001 0
InputContinuousHydrationLane 2 0000000000000000000000000000010 1
InputContinuousLane 4 0000000000000000000000000000100 2
DefaultHydrationLane 8 0000000000000000000000000001000 3
DefaultLane 16 0000000000000000000000000010000 4
TransitionHydrationLane 32 0000000000000000000000000100000 5
TransitionLane1 64 0000000000000000000000001000000 6
TransitionLane2 128 0000000000000000000000010000000 7
TransitionLane3 256 0000000000000000000000100000000 8
TransitionLane4 512 0000000000000000000001000000000 9
TransitionLane5 1024 0000000000000000000010000000000 10
TransitionLane 2048 0000000000000000000100000000000 11
TransitionLane7 4096 0000000000000000001000000000000 12
TransitionLane8 8192 0000000000000000010000000000000 13
TransitionLane9 16384 0000000000000000100000000000000 14
TransitionLane10 32768 0000000000000001000000000000000 15
TransitionLane11 65536 0000000000000010000000000000000 16
TransitionLane12 131072 0000000000000100000000000000000 17
TransitionLane13 262144 0000000000001000000000000000000 18
TransitionLane14 524288 0000000000010000000000000000000 19
TransitionLane15 1048576 0000000000100000000000000000000 20
TransitionLane16 2097152 0000000001000000000000000000000 21
RetryLane1 4194304 0000000010000000000000000000000 22
RetryLane2 8388608 0000000100000000000000000000000 23
RetryLane3 16777216 0000001000000000000000000000000 24
RetryLane4 33554432 0000010000000000000000000000000 25
RetryLane5 67108864 0000100000000000000000000000000 26
SelectiveHydrationLane 134217728 0001000000000000000000000000000 27
IdleHydrationLane 268435456 0010000000000000000000000000000 28
IdleLane 536870912 0100000000000000000000000000000 29
OffscreenLane 1073741824 1000000000000000000000000000000 30

приоритет события

EventPriority Lane Численная величина
DiscreteEventPriority дискретные события. Щелчок, нажатие клавиши, фокусировка и т. Д., Запуск событий не является непрерывным, и можно добиться быстрого реагирования. SyncLane 1
ContinuousEventPriority последовательные события. Перетаскивание, прокрутка, наведение мыши и т. д., события запускаются постоянно, быстрый отклик может блокировать рендеринг, а приоритет ниже, чем у дискретных событий. InputContinuousLane 4
DefaultEventPriority Приоритет события по умолчанию DefaultLane 16
IdleEventPriority приоритет бездействия IdleLane 536870912

приоритет планировщика

SchedulerPriority EventPriority Больше >17.0.2 Менее >17.0.2
ImmediatePriority DiscreteEventPriority 1 99
UserblockingPriority Userblocking 2 98
NormalPriority DefaultEventPriority 3 97
LowPriority DefaultEventPriority 4 96
IdlePriority IdleEventPriority 5 95
NoPriority 0 90

переход между приоритетами

  • приоритет полосы перед приоритетом события (см.lanesToEventPriorityфункция)

    • Правило преобразования: возвращает соответствующий приоритет события в виде интервала согласно входящей дорожке. Например, если входящий приоритет не превышает дискретного приоритета, будет возвращен дискретный приоритет и так далее.
  • приоритет события перед приоритетом планировщика (см.ensureRootIsScheduledфункция)

    • Правила преобразования: вы можете обратиться к таблице приоритетов планировщика выше.
  • приоритет события для приоритета полосы движения (см.getEventPriorityфункция)

    • Правила преобразования: Для недискретных и непрерывных событий они будут преобразованы в соответствии с определенными правилами.Для конкретных уроков, пожалуйста, обратитесь к таблице приоритетов событий выше.

Как устроен механизм приоритета

Когда речь идет о приоритетном механизме, мы скоро сможем думать о том, что это приоритетная очередь, его самая выдающаяся функциясначала наивысший приоритет,reactМеханизм приоритета подобен очереди приоритетов, но он используетгоночная трассаконцепция, подходитпобитовое ИФункция очереди обогащена. По сравнению с очередью приоритета, скорость чтения и записи быстрее и легче понять.

Идеи дизайна

  1. Объединить треки: поддерживать очередь, в которой можно хранить занятые треки
  2. Освободить дорожку: освободить соответствующую занятую дорожку в соответствии с приоритетом.
  3. Найти дорожку с наивысшим приоритетом: получить дорожку с наивысшим приоритетом в очереди.
  4. Быстро найти индекс трека: получить позицию трека в очереди в соответствии с приоритетом
  5. Определить, занята ли дорожка: по входящему приоритету определить, занята ли дорожка, на которой находится приоритет

объединить трек

  • Сцены
    • Например, приоритет текущей запланированной задачи — DefaultLane, пользователь щелкает, чтобы запустить обновление, генерируется задача с высоким приоритетом SyncLane, и необходимо сохранить дорожку, занимаемую этой задачей.
  • Процесс работы
    • Метод операции: побитовая операция ИЛИ -a | b
    • Результат операции: DefaultLane и SyncLane занимают 1-ю и 5-ю дорожки соответственно.
DefaultLane优先级为16,SyncLane优先级为1

16 | 1 = 17

17的二进制值为10001
16的二进制值为10000,1的二进制值为00001

бесплатный трек

  • Сцены
    • После выполнения задачи SyncLane занятую дорожку необходимо освободить
  • Процесс работы
    • Режим работы: бит и + бит не -a & ~b
    • Результат операции: дорожка SyncLane освобождается, остается только дорожка DefaultLane.
17 & ~1 = 16
17的二进制值为10001

为什么用位非?
~1 = -2
2 的二进制是00010,-2的话符号位取反变为10010
10001和10010进行位与运算得到10000,也就是十进制的16

Найти дорожку с наивысшим приоритетом

  • Сцены
    • В настоящее время дорожку занимают две приоритетные задачи, DefaultLane и SyncLane.После входа в метод sureRootIsScheduled мне нужно сначала запланировать задачу с наивысшим приоритетом, поэтому мне нужно найти дорожку с наивысшим приоритетом.
  • Процесс работы
    • Режим работы: бит и знак + отрицание бита -a & -b
    • Результат операции: Найдена задача SyncLane с наивысшим приоритетом. Задача SyncLane является задачей синхронизации. Планировщик запланирует текущий корневой узел приложения с приоритетом синхронизации.
17 & -17 = 1

17的二进制值为10001
-17的二进制值为00001
10001和00001进行位与运算得到1,也就是SyncLane

Быстро найти индекс трека

  • Сцены
    • Пробуждение голодной задачи: перед началом планирования нам необходимо оценить все дорожки в очереди, чтобы определить, истек ли срок действия задачи дорожки.Если срок ее действия истек, просроченная задача будет выполнена первой. Для этого необходимо поддерживать массив длиной 31. Нижний индекс каждого элемента массива соответствует 31 приоритетной дорожке один к одному.Срок действия задачиПри оценке мы надеемся быстро найти позицию, соответствующую массиву по приоритету.
  • Процесс работы
    • Метод работы: Math.clz32
    • Результат операции: если позиция индекса DefaultLane равна 4, то eventTimes и expireTimes на корневом узле приложения могут быть освобождены, а значение его местоположения назначено на -1, а затем соответствующая задача с истекшим сроком действия может быть освобождена. быть казненным.
// 找出 DefaultLane 赛道索引
31 - Math.clz32(16) = 4

16的二进制值为10000
索引4对应的就是第五个赛道
  • Для чего используется Math.clz32?
    • Получает количество начальных нулей в двоичном значении, соответствующем десятичному числу.
    • Так что вычтите это из 31Math.clz32Значение , вы можете получить индекс трека

Определить, занята ли дорожка

Ссылаться наcodesandboxВышеприведенный код,в асинхронном режимеБудут приоритетные задачи, прерывающие очередь, в этом случаеstateМетод расчета будет следующимв режиме синхронизациинемного отличается.

  • Сцены
    1. Мы не обновляем сразу после установки состоянияstate, но будет генерировать на основе содержимого setStateUpdateObject, этот объект содержит такие свойства, как содержимое обновления и приоритет обновления.
    2. возобновитьstateЭто действие вprocessUpdateQueueВ функции функция будет судитьUpdateЗанята ли дорожка, на которой находится приоритет объекта, чтобы решить, считать ли это в этом раунде задачUpdateобъектstate
      • Если занято, представительUpdateПриоритет объекта равен выполняемой в данный момент задаче и может быть определен согласноUpdateобъектные вычисленияstateи обновить до узла FibermemoizedStateатрибут
      • Если он не занят, это означает, что приоритет выполняемой в данный момент задачи выше, чем этотUpdateОбъект имеет высокий приоритет, а соответствующий низкий приоритетUpdateСостояние объекта временно не будет рассчитываться, и он будет рассчитан при перезапуске следующего раунда низкоприоритетных задач.
  • Процесс работы
    • Метод работы: бит и(renderLanes & updateLanes) == updateLanes
    • Результат операции: 0 означает, что текущий приоритет планирования выше, чем приоритет объекта Update.
运算公式
(1 & 16) == 16

1的二进制值为00001
16的二进制值为10000 
00001和10000进行位与运算得到0

Как внедрить механизмы приоритетов в среду выполнения React

Создать задачу обновления

Процесс генерации задач на самом деле очень прост, а вход находится в нашем обычномsetStateфункция, первая

setState.png

setStateТо, что выполняется внутри функции,enqueueUpdateфункция, в то время какenqueueUpdateРабота функции в основном делится на 4 шага:

  1. Получить приоритет этого обновления.
  2. СоздайтеUpdateобъект
  3. Свяжите этот приоритет обновления с текущим узлом Fiber, родительским узлом и корневым узлом приложения.
  4. положить началоensureRootIsScheduledрасписание.

Шаг первый: получите этот приоритет обновления

Задача первого шага — позвонитьrequestUpdateLaneФункция получает приоритет задачи обновления

  1. Если ток неconcurrentмодель
    • В настоящее время не находится в стадии рендеринга. вернуть синхронную полосу
    • Сейчас на этапе рендеринга. Вернуть наивысший приоритет в workInProgressRootRenderLanes (здесь используется вышеуказанный механизм расчета приоритета,Найти дорожку с наивысшим приоритетом)
  2. если в настоящее времяconcurrentмодель
    • Если вам нужно выполнить отложенные задачи, такие какSuspend,useTransition,useDefferedValueи другие характеристики. существуетtransitionВведите приоритет, чтобы найти бесплатные треки.transitionЕсть 16 типов трасс, от 1 до 16 статьи, при заезде на 16 трассу в следующий разtransitionТиповые миссии возвращаются к дорожке 1 и так далее.
    • воплощать в жизньgetCurrentUpdatePriorityфункция. Получить текущий приоритет обновления. если неNoLaneпросто вернись
    • воплощать в жизньgetCurrentEventPriorityфункция. Возвращает текущий приоритет события. Если событие не происходит, вернитеDefaultEventPriority

В основном,requestUpdateLaneПорядок приоритетного выбора и оценки функций следующий:

SyncLane  >>  TransitionLane  >>  UpdateLane  >>  EventLane

Подсчитано, что многие мелкие партнеры будут очень смущены вопросом, почему так много функций для получения приоритета, здесь я разобрался с обязанностями других функций.

image.png

Шаг 2: Создайте объект обновления

Кода тут немного, по сути, нужно инкапсулировать параметры setState в объект и оставить это на этапе рендеринга.

function createUpdate(eventTime, lane) {
  var update = {
    eventTime: eventTime,
    lane: lane,
    tag: UpdateState,
    payload: null,
    callback: null,
    next: null
  };
  return update;
}

Шаг 3: Связать приоритет

Здесь сначала объясните два понятия, одно из которыхHostRoot,одинFiberRootNode

  • HostRoot:то естьReactDOM.renderПервый параметр корневого узла дерева компонентов.HostRootИх может быть больше одного, потому чтоReactDOM.renderМожно вызывать несколько раз
  • FiberRootNode: корневой узел приложения React, каждая страница имеет только один корневой узел приложения React. Доступна сHostRootузлаstateNodeдоступ к собственности

Здесь ассоциированный приоритет в основном выполняет две функции

  1. markUpdateLaneFromFiberToRoot. Эта функция в основном делает две вещи
    • Объедините приоритет со свойством lanes текущего узла Fiber.
    • Включите приоритет в свойство childLanes родителя (сообщает родителю, сколько дорожек должен запустить его ребенок)
    Но поскольку узел Fiber, переданный функцией,HostRoot, то есть,ReactDOM.renderКорневой узел , то есть родительского узла нет, поэтому второе не делается.
  2. markRootUpdated. Эта функция также в основном делает две вещи
    • Объедините приоритет задач, которые должны быть запланированы, с корневым узлом текущего реагирующего приложения.
    • Рассчитать время начала (eventTime), занимаемое текущей дорожкой приоритета задачи

Отсюда видно, чтоreactМеханизм приоритета не работает независимо в каждом узле компонента, а опирается на глобальныйreactПримените корневой узел для управления планированием задач следующих множественных деревьев компонентов.

БудуприоритетКакая польза от связи с этими узлами Fiber?

Давайте поговорим о нихразница

  • дорожки: существуют только на корневом узле нереагирующих приложений, запишите приоритет дорожки текущего узла волокна
  • childLanes: существует только на корневом узле нереагирующих приложений и записывает приоритет полосы всех дочерних волоконных узлов под текущим волоконным узлом.
  • PendendingLanes: существует только на корневом узле приложения React, и записывает всеHostRootприоритет полосы движения

специфическийСценарии применения

  1. Отпустите трек. В упомянутом выше механизме расчета приоритета упоминается, что дорожка будет освобождена после выполнения задачи, в частности, занятый приоритет будет освобожден после завершения фазы фиксации, т. е.markRootFinishedфункция.
  2. Определите, занята ли дорожка. на этапе рендерингаbeginWorkВ процессе будет много сужденийchildLanesРешение о том, занято ли оно

Шаг 4: Начните планирование

Самый важный шаг в планированииensureRootIsScheduledВызов функции, логика функции состоит из следующих двух частей:Задачи с высоким приоритетом прерывают задачи с низким приоритетома такжепроблема голодной миссии

Задачи с высоким приоритетом прерывают задачи с низким приоритетом

Эту часть процесса можно разделить на три части

  1. cancelCallback
  2. pop(taskQueue)
  3. Перезапуск задачи с низким приоритетом

cancelCallback

var existingCallbackNode = root.callbackNode;
var existingCallbackPriority = root.callbackPriority;
var newCallbackPriority = getHighestPriorityLane(nextLanes);
if (existingCallbackPriority === newCallbackPriority) {
    ...
    return;
}
if (existingCallbackNode != null) {
    cancelCallback(existingCallbackNode);
}

newCallbackNode = scheduleCallback(
    schedulerPriorityLevel,
    performConcurrentWorkOnRoot.bind(null, root)
);
root.callbackPriority = newCallbackPriority;
root.callbackNode = newCallbackNode;

вышеensureRootIsScheduledНекоторые фрагменты кода функции, сначала объясните переменные

  • existingCallbackNode: задача выполняется на текущем этапе рендеринга.
  • existingCallbackPriority: приоритет текущих задач на текущем этапе рендеринга.
  • newCallbackPriority: этот приоритет планирования

будем судить здесьexistingCallbackPriorityа такжеnewCallbackPriorityРавны ли два приоритета,если равны, это обновление объединяется с текущей задачей.если не равно, что означает, что приоритет этой задачи обновления выше и должен прервать текущую задачу

Как прервать миссию?

  1. ключевая функцияcancelCallback(existingCallbackNode),cancelCallbackфункция состоит в том, чтобыroot.callbackNodeприсвоить значение null
  2. performConcurrentWorkOnRootФункция будет сначалаroot.callbackNodeКэшируйте его, и он будет оценен в конце функцииroot.callbackNodeЭто то же самое, что значение, которое было изначально кэшировано, если нет, значитroot.callbackNodeЕй присваивается значение null, и в дело вступает задача с более высоким приоритетом.
  3. В настоящее времяperformConcurrentWorkOnRootвозвращаемое значение равно нулю

НижеperformConcurrentWorkOnRootсегмент кода

...
var originalCallbackNode = root.callbackNode;
...
// 函数末尾
if (root.callbackNode === originalCallbackNode) {
    return performConcurrentWorkOnRoot.bind(null, root);
}
return null;

Из вышеперечисленногоensureRootIsScheduledФрагмент кода может знать,performConcurrentWorkOnRootфункцияscheduleCallbackФункция запланирована, и логика после конкретного возврата должна перейти кSchedulerмодуль для поиска

pop(taskQueue)

var callback = currentTask.callback;
if (typeof callback === 'function') {
  ...
} else {
  pop(taskQueue);
}

вышеSchedulerвнутри модуляworkLoopфрагмент кода для функции,currentTask.callbackто естьscheduleCallbackВторой параметр , которыйperformConcurrentWorkOnRootфункция

В продолжение предыдущей темы, еслиperformConcurrentWorkOnRootФункция вернула ноль,workLoopбудет выполняться внутриpop(taskQueue), измените текущую задачу сtaskQueueВо всплывающем окне.

Перезапуск задачи с низким приоритетом

На предыдущем шаге было сказано, что задача с низким приоритетом начинается сtaskQueueвыбрасывается. Как после выполнения задачи с высоким приоритетом перезапустить предыдущую задачу с низким приоритетом?

Ключ вcommitRootImplфункция

var remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);
markRootFinished(root, remainingLanes);

...

ensureRootIsScheduled(root, now());

markRootFinishedТолько что упомянутая выше функция освобождает дорожку, занятую завершенной задачей, а это означает, что незавершенная задача по-прежнему будет занимать свою дорожку, поэтому мы можем вызвать ее снова.ensureRootIsScheduledЗапустите новое расписание, чтобы перезапустить выполнение низкоприоритетных задач. Мы можем перезапустить часть решения

var nextLanes = getNextLanes(
    root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes
);
// 如果 nextLanes 为 NoLanes,就证明所有任务都执行完毕了
if (nextLanes === NoLanes) {
    ...
    root.callbackNode = null;
    root.callbackPriority = NoLane;
    // 只要 nextLanes 为 NoLanes,就可以结束调度了
    return;
}
// 如果 nextLanes 不为 NoLanes,就代表还有任务未执行完,也就是那些被打断的低优先级任务
...

проблема голодной миссии

Как упоминалось выше, после выполнения задачи с высоким приоритетом будет перезапущена задача с низким приоритетом, но если есть задачи с высоким приоритетом, которые продолжают поступать, разве моя задача с низким приоритетом не перезапускается?

Так что реагируйте, чтобы разобраться с проблемой решения голодных задач, реагируйте вensureRootIsScheduledКогда функция запускается, выполняется следующая обработка: (см.markStarvedLanesAsExpiredфункция)

var lanes = pendingLanes;
while (lanes > 0) {
    var index = pickArbitraryLaneIndex(lanes);
    var lane = 1 << index;
    var expirationTime = expirationTimes[index];
    if (expirationTime === NoTimestamp) {
      if ((lane & suspendedLanes) === NoLanes || (lane & pingedLanes) !== NoLanes) {
        expirationTimes[index] = computeExpirationTime(lane, currentTime);
      }
    } else if (expirationTime <= currentTime) {
      root.expiredLanes |= lane;
    }
    lanes &= ~lane;
}
  1. Пройдите 31 трек, чтобы определить, соответствует ли время истечения срока действия каждого трека.NoTimestamp, если да, и на треке есть задачи для выполнения, инициализировать время истечения для трека
  2. Если у дорожки есть время истечения, и время истечения меньше текущего времени, значит, срок действия задачи истек, и текущий приоритет нужно объединить вexpiredLanes, так что в следующем раунде фазы рендеринга текущийHostRoot

Вы можете обратиться к функции, выполняемой на этапе рендеринга.performConcurrentWorkOnRootфрагмент кода в

var exitStatus = shouldTimeSlice(root, lanes) && ( !didTimeout) ? 
                    renderRootConcurrent(root, lanes) : 
                    renderRootSync(root, lanes);

можно увидеть, покаshouldTimeSliceПока он возвращает false, он будет выполнятьсяrenderRootSync, то есть войдите в фазу рендеринга с синхронным приоритетом. а такжеshouldTimeSliceЛогика простоexpiredLanesсвязанный с атрибутом

function shouldTimeSlice(root, lanes) {
  // 如果 expiredLanes 里面有东西,代表有饥饿任务
  if ((lanes & root.expiredLanes) !== NoLanes) {
    return false;
  }
  var SyncDefaultLanes = InputContinuousHydrationLane | 
                          InputContinuousLane | 
                          DefaultHydrationLane | 
                          DefaultLane;

  return (lanes & SyncDefaultLanes) === NoLanes;
}

Суммировать

reactМеханизм приоритета не является независимым и несвязанным модулем в исходном коде, а включает в себя все аспекты общей работы реакции Наконец, мы возвращаемся к разбору использования механизма приоритета в исходном коде, чтобы каждый мог иметь лучшее понимание механизма приоритета общее восприятие.

  1. разрезание времени. Включение прерывания задачи, расчет продолжительности фрагментации в соответствии с приоритетом
  2. setState генерирует объект Update. Каждый объект обновления имеет атрибут полосы, который представляет приоритет обновления.
  3. Задачи с высоким приоритетом прерывают задачи с низким приоритетом. Каждое планирование будет сравнивать текущую задачу с наивысшим приоритетом текущей задачи.Если они не равны, это означает, что поступает задача с высоким приоритетом, и текущую задачу необходимо прервать.
  4. 低优先级任务重启。 координация(reconcile)Следующий этап — рендеринг(renderer), что мы называем фазой фиксации. В конце этой фазы он вызоветensureRootIsScheduledИнициируйте новое расписание для выполнения ожидающих выполнения задач с низким приоритетом.
  5. Просыпается голодная миссия. В начале каждого планирования он сначала проверяет, есть ли задачи с истекшим сроком действия, и если они есть, в следующий раз будет синхронный приоритет.(reconcile), приоритет синхронизации является наивысшим приоритетом и не будет прерываться

В конце концов, общая карта разума реакции разобрана, модуль планировщика в основном разобрался, а остальное будет сосредоточено на фазе рендеринга и фазе фиксации.

react.png