Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/renderers/dom/fiber/ReactDOMFiber.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ var DOMRenderer = ReactFiberReconciler({
// Noop
},

scheduleHighPriCallback: window.requestAnimationFrame,
scheduleAnimationCallback: window.requestAnimationFrame,

scheduleLowPriCallback: window.requestIdleCallback,
scheduleDeferredCallback: window.requestIdleCallback,

});

Expand Down
35 changes: 21 additions & 14 deletions src/renderers/noop/ReactNoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@ import type { UpdateQueue } from 'ReactFiberUpdateQueue';
import type { HostChildren } from 'ReactFiberReconciler';

var ReactFiberReconciler = require('ReactFiberReconciler');
var {
AnimationPriority,
} = require('ReactPriorityLevel');

var scheduledHighPriCallback = null;
var scheduledLowPriCallback = null;
var scheduledAnimationCallback = null;
var scheduledDeferredCallback = null;

const TERMINAL_TAG = 99;

Expand Down Expand Up @@ -88,12 +91,12 @@ var NoopRenderer = ReactFiberReconciler({
deleteInstance(instance : Instance) : void {
},

scheduleHighPriCallback(callback) {
scheduledHighPriCallback = callback;
scheduleAnimationCallback(callback) {
scheduledAnimationCallback = callback;
},

scheduleLowPriCallback(callback) {
scheduledLowPriCallback = callback;
scheduleDeferredCallback(callback) {
scheduledDeferredCallback = callback;
},

});
Expand All @@ -114,21 +117,21 @@ var ReactNoop = {
}
},

flushHighPri() {
var cb = scheduledHighPriCallback;
flushAnimationPri() {
var cb = scheduledAnimationCallback;
if (cb === null) {
return;
}
scheduledHighPriCallback = null;
scheduledAnimationCallback = null;
cb();
},

flushLowPri(timeout : number = Infinity) {
var cb = scheduledLowPriCallback;
flushDeferredPri(timeout : number = Infinity) {
var cb = scheduledDeferredCallback;
if (cb === null) {
return;
}
scheduledLowPriCallback = null;
scheduledDeferredCallback = null;
var timeRemaining = timeout;
cb({
timeRemaining() {
Expand All @@ -143,8 +146,12 @@ var ReactNoop = {
},

flush() {
ReactNoop.flushHighPri();
ReactNoop.flushLowPri();
ReactNoop.flushAnimationPri();
ReactNoop.flushDeferredPri();
},

performAnimationWork(fn: Function) {
NoopRenderer.performWithPriority(AnimationPriority, fn);
},

// Logs the current state of the tree.
Expand Down
4 changes: 2 additions & 2 deletions src/renderers/shared/fiber/ReactFiberBeginWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ module.exports = function<T, P, I, C>(config : HostConfig<T, P, I, C>, getSchedu
}

function scheduleUpdate(fiber: Fiber, updateQueue: UpdateQueue, priorityLevel : PriorityLevel): void {
const { scheduleLowPriWork } = getScheduler();
const { scheduleDeferredWork } = getScheduler();
fiber.updateQueue = updateQueue;
// Schedule update on the alternate as well, since we don't know which tree
// is current.
Expand All @@ -138,7 +138,7 @@ module.exports = function<T, P, I, C>(config : HostConfig<T, P, I, C>, getSchedu
// Duck type root
if (fiber.stateNode && fiber.stateNode.containerInfo) {
const root : FiberRoot = (fiber.stateNode : any);
scheduleLowPriWork(root, priorityLevel);
scheduleDeferredWork(root, priorityLevel);
return;
}
if (!fiber.return) {
Expand Down
23 changes: 10 additions & 13 deletions src/renderers/shared/fiber/ReactFiberReconciler.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,11 @@
import type { Fiber } from 'ReactFiber';
import type { FiberRoot } from 'ReactFiberRoot';
import type { TypeOfWork } from 'ReactTypeOfWork';
import type { PriorityLevel } from 'ReactPriorityLevel';

var { createFiberRoot } = require('ReactFiberRoot');
var ReactFiberScheduler = require('ReactFiberScheduler');

var {
LowPriority,
} = require('ReactPriorityLevel');

type Deadline = {
timeRemaining : () => number
};
Expand All @@ -44,8 +41,8 @@ export type HostConfig<T, P, I, C> = {
commitUpdate(instance : I, oldProps : P, newProps : P, children : HostChildren<I>) : void,
deleteInstance(instance : I) : void,

scheduleHighPriCallback(callback : () => void) : void,
scheduleLowPriCallback(callback : (deadline : Deadline) => void) : void
scheduleAnimationCallback(callback : () => void) : void,
scheduleDeferredCallback(callback : (deadline : Deadline) => void) : void

};

Expand All @@ -55,14 +52,15 @@ export type Reconciler<C> = {
mountContainer(element : ReactElement<any>, containerInfo : C) : OpaqueNode,
updateContainer(element : ReactElement<any>, container : OpaqueNode) : void,
unmountContainer(container : OpaqueNode) : void,
performWithPriority(priorityLevel : PriorityLevel, fn : Function) : void,

// Used to extract the return value from the initial render. Legacy API.
getPublicRootInstance(container : OpaqueNode) : (C | null),
};

module.exports = function<T, P, I, C>(config : HostConfig<T, P, I, C>) : Reconciler<C> {

var { scheduleLowPriWork } = ReactFiberScheduler(config);
var { scheduleWork, performWithPriority } = ReactFiberScheduler(config);

return {

Expand All @@ -73,9 +71,8 @@ module.exports = function<T, P, I, C>(config : HostConfig<T, P, I, C>) : Reconci
// TODO: This should not override the pendingWorkPriority if there is
// higher priority work in the subtree.
container.pendingProps = element;
container.pendingWorkPriority = LowPriority;

scheduleLowPriWork(root, LowPriority);
scheduleWork(root);

// It may seem strange that we don't return the root here, but that will
// allow us to have containers that are in the middle of the tree instead
Expand All @@ -88,21 +85,21 @@ module.exports = function<T, P, I, C>(config : HostConfig<T, P, I, C>) : Reconci
const root : FiberRoot = (container.stateNode : any);
// TODO: Use pending work/state instead of props.
root.current.pendingProps = element;
root.current.pendingWorkPriority = LowPriority;

scheduleLowPriWork(root, LowPriority);
scheduleWork(root);
},

unmountContainer(container : OpaqueNode) : void {
// TODO: If this is a nested container, this won't be the root.
const root : FiberRoot = (container.stateNode : any);
// TODO: Use pending work/state instead of props.
root.current.pendingProps = [];
root.current.pendingWorkPriority = LowPriority;

scheduleLowPriWork(root, LowPriority);
scheduleWork(root);
},

performWithPriority,

getPublicRootInstance(container : OpaqueNode) : (C | null) {
return null;
},
Expand Down
99 changes: 85 additions & 14 deletions src/renderers/shared/fiber/ReactFiberScheduler.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,15 @@ var { cloneFiber } = require('ReactFiber');

var {
NoWork,
LowPriority,
AnimationPriority,
SynchronousPriority,
} = require('ReactPriorityLevel');

var timeHeuristicForUnitOfWork = 1;

export type Scheduler = {
scheduleLowPriWork: (root : FiberRoot, priority : PriorityLevel) => void
scheduleDeferredWork: (root : FiberRoot, priority : PriorityLevel) => void
};

module.exports = function<T, P, I, C>(config : HostConfig<T, P, I, C>) {
Expand All @@ -45,8 +48,11 @@ module.exports = function<T, P, I, C>(config : HostConfig<T, P, I, C>) {
const { completeWork } = ReactFiberCompleteWork(config);
const { commitWork } = ReactFiberCommitWork(config);

// const scheduleHighPriCallback = config.scheduleHighPriCallback;
const scheduleLowPriCallback = config.scheduleLowPriCallback;
const scheduleAnimationCallback = config.scheduleAnimationCallback;
const scheduleDeferredCallback = config.scheduleDeferredCallback;

// The default priority to use for updates.
let defaultPriority : PriorityLevel = LowPriority;

// The next work in progress fiber that we're currently working on.
let nextUnitOfWork : ?Fiber = null;
Expand Down Expand Up @@ -217,7 +223,7 @@ module.exports = function<T, P, I, C>(config : HostConfig<T, P, I, C>) {
}
}

function performLowPriWork(deadline) {
function performDeferredWork(deadline) {
if (!nextUnitOfWork) {
nextUnitOfWork = findNextUnitOfWork();
}
Expand All @@ -229,20 +235,68 @@ module.exports = function<T, P, I, C>(config : HostConfig<T, P, I, C>) {
nextUnitOfWork = findNextUnitOfWork();
}
} else {
scheduleLowPriCallback(performLowPriWork);
scheduleDeferredCallback(performDeferredWork);
return;
}
}
}

function scheduleLowPriWork(root : FiberRoot, priority : PriorityLevel) {
function scheduleDeferredWork(root : FiberRoot, priority : PriorityLevel) {
// We must reset the current unit of work pointer so that we restart the
// search from the root during the next tick, in case there is now higher
// priority work somewhere earlier than before.
if (priority <= nextPriorityLevel) {
nextUnitOfWork = null;
}

// Set the priority on the root, without deprioritizing
if (root.current.pendingWorkPriority === NoWork ||
priority <= root.current.pendingWorkPriority) {
root.current.pendingWorkPriority = priority;
}

if (root.isScheduled) {
// If we're already scheduled, we can bail out.
return;
}
root.isScheduled = true;
if (lastScheduledRoot) {
// Schedule ourselves to the end.
lastScheduledRoot.nextScheduledRoot = root;
lastScheduledRoot = root;
} else {
// We're the only work scheduled.
nextScheduledRoot = root;
lastScheduledRoot = root;
scheduleDeferredCallback(performDeferredWork);
}
}

function performAnimationWork() {
// Always start from the root
nextUnitOfWork = findNextUnitOfWork();
while (nextUnitOfWork &&
nextPriorityLevel !== NoWork) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
if (!nextUnitOfWork) {
// Keep searching for animation work until there's no more left
nextUnitOfWork = findNextUnitOfWork();
}
// Stop if the next unit of work is low priority
if (nextPriorityLevel > AnimationPriority) {
scheduleDeferredCallback(performDeferredWork);
return;
}
}
}

function scheduleAnimationWork(root: FiberRoot, priorityLevel : PriorityLevel) {
// Set the priority on the root, without deprioritizing
if (root.current.pendingWorkPriority === NoWork ||
priorityLevel <= root.current.pendingWorkPriority) {
root.current.pendingWorkPriority = priorityLevel;
}

if (root.isScheduled) {
// If we're already scheduled, we can bail out.
return;
Expand All @@ -256,22 +310,39 @@ module.exports = function<T, P, I, C>(config : HostConfig<T, P, I, C>) {
// We're the only work scheduled.
nextScheduledRoot = root;
lastScheduledRoot = root;
scheduleLowPriCallback(performLowPriWork);
scheduleAnimationCallback(performAnimationWork);
}
}

/*
function performHighPriWork() {
// There is no such thing as high pri work yet.
function scheduleWork(root : FiberRoot) {
if (defaultPriority === SynchronousPriority) {
throw new Error('Not implemented yet');
}

if (defaultPriority === NoWork) {
return;
}
if (defaultPriority > AnimationPriority) {
scheduleDeferredWork(root, defaultPriority);
return;
}
scheduleAnimationWork(root, defaultPriority);
}

function ensureHighPriIsScheduled() {
scheduleHighPriCallback(performHighPriWork);
function performWithPriority(priorityLevel : PriorityLevel, fn : Function) {
const previousDefaultPriority = defaultPriority;
defaultPriority = priorityLevel;
try {
fn();
} finally {
defaultPriority = previousDefaultPriority;
}
}
*/

scheduler = {
scheduleLowPriWork: scheduleLowPriWork,
scheduleWork: scheduleWork,
scheduleDeferredWork: scheduleDeferredWork,
performWithPriority: performWithPriority,
};
return scheduler;
};
Loading