wallet-core/node_modules/react-dom/lib/ReactFiberScheduler.js

331 lines
12 KiB
JavaScript
Raw Normal View History

2017-05-03 15:35:00 +02:00
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
*/
'use strict';
var ReactFiberBeginWork = require('./ReactFiberBeginWork');
var ReactFiberCompleteWork = require('./ReactFiberCompleteWork');
var ReactFiberCommitWork = require('./ReactFiberCommitWork');
var _require = require('./ReactFiber'),
cloneFiber = _require.cloneFiber;
var _require2 = require('./ReactPriorityLevel'),
NoWork = _require2.NoWork,
LowPriority = _require2.LowPriority,
AnimationPriority = _require2.AnimationPriority,
SynchronousPriority = _require2.SynchronousPriority;
var timeHeuristicForUnitOfWork = 1;
module.exports = function (config) {
// Use a closure to circumvent the circular dependency between the scheduler
// and ReactFiberBeginWork. Don't know if there's a better way to do this.
var scheduler = void 0;
function getScheduler() {
return scheduler;
}
var _ReactFiberBeginWork = ReactFiberBeginWork(config, getScheduler),
beginWork = _ReactFiberBeginWork.beginWork;
var _ReactFiberCompleteWo = ReactFiberCompleteWork(config),
completeWork = _ReactFiberCompleteWo.completeWork;
var _ReactFiberCommitWork = ReactFiberCommitWork(config),
commitWork = _ReactFiberCommitWork.commitWork;
var scheduleAnimationCallback = config.scheduleAnimationCallback;
var scheduleDeferredCallback = config.scheduleDeferredCallback;
// The default priority to use for updates.
var defaultPriority = LowPriority;
// The next work in progress fiber that we're currently working on.
var nextUnitOfWork = null;
var nextPriorityLevel = NoWork;
// Linked list of roots with scheduled work on them.
var nextScheduledRoot = null;
var lastScheduledRoot = null;
function findNextUnitOfWork() {
// Clear out roots with no more work on them.
while (nextScheduledRoot && nextScheduledRoot.current.pendingWorkPriority === NoWork) {
nextScheduledRoot.isScheduled = false;
if (nextScheduledRoot === lastScheduledRoot) {
nextScheduledRoot = null;
lastScheduledRoot = null;
nextPriorityLevel = NoWork;
return null;
}
nextScheduledRoot = nextScheduledRoot.nextScheduledRoot;
}
// TODO: This is scanning one root at a time. It should be scanning all
// roots for high priority work before moving on to lower priorities.
var root = nextScheduledRoot;
var highestPriorityRoot = null;
var highestPriorityLevel = NoWork;
while (root) {
if (highestPriorityLevel === NoWork || highestPriorityLevel > root.current.pendingWorkPriority) {
highestPriorityLevel = root.current.pendingWorkPriority;
highestPriorityRoot = root;
}
// We didn't find anything to do in this root, so let's try the next one.
root = root.nextScheduledRoot;
}
if (highestPriorityRoot) {
nextPriorityLevel = highestPriorityLevel;
return cloneFiber(highestPriorityRoot.current, highestPriorityLevel);
}
nextPriorityLevel = NoWork;
return null;
}
function commitAllWork(finishedWork) {
// Commit all the side-effects within a tree.
// TODO: Error handling.
var effectfulFiber = finishedWork.firstEffect;
while (effectfulFiber) {
var current = effectfulFiber.alternate;
commitWork(current, effectfulFiber);
var next = effectfulFiber.nextEffect;
// Ensure that we clean these up so that we don't accidentally keep them.
// I'm not actually sure this matters because we can't reset firstEffect
// and lastEffect since they're on every node, not just the effectful
// ones. So we have to clean everything as we reuse nodes anyway.
effectfulFiber.nextEffect = null;
effectfulFiber = next;
}
}
function resetWorkPriority(workInProgress) {
var newPriority = NoWork;
// progressedChild is going to be the child set with the highest priority.
// Either it is the same as child, or it just bailed out because it choose
// not to do the work.
var child = workInProgress.progressedChild;
while (child) {
// Ensure that remaining work priority bubbles up.
if (child.pendingWorkPriority !== NoWork && (newPriority === NoWork || newPriority > child.pendingWorkPriority)) {
newPriority = child.pendingWorkPriority;
}
child = child.sibling;
}
workInProgress.pendingWorkPriority = newPriority;
}
function completeUnitOfWork(workInProgress) {
while (true) {
// The current, flushed, state of this fiber is the alternate.
// Ideally nothing should rely on this, but relying on it here
// means that we don't need an additional field on the work in
// progress.
var current = workInProgress.alternate;
var next = completeWork(current, workInProgress);
resetWorkPriority(workInProgress);
// The work is now done. We don't need this anymore. This flags
// to the system not to redo any work here.
workInProgress.pendingProps = null;
workInProgress.updateQueue = null;
var returnFiber = workInProgress['return'];
if (returnFiber) {
// Ensure that the first and last effect of the parent corresponds
// to the children's first and last effect. This probably relies on
// children completing in order.
if (!returnFiber.firstEffect) {
returnFiber.firstEffect = workInProgress.firstEffect;
}
if (workInProgress.lastEffect) {
if (returnFiber.lastEffect) {
returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;
}
returnFiber.lastEffect = workInProgress.lastEffect;
}
}
if (next) {
// If completing this work spawned new work, do that next.
return next;
} else if (workInProgress.sibling) {
// If there is more work to do in this returnFiber, do that next.
return workInProgress.sibling;
} else if (returnFiber) {
// If there's no more work in this returnFiber. Complete the returnFiber.
workInProgress = returnFiber;
continue;
} else {
// If we're at the root, there's no more work to do. We can flush it.
var _root = workInProgress.stateNode;
if (_root.current === workInProgress) {
throw new Error('Cannot commit the same tree as before. This is probably a bug ' + 'related to the return field.');
}
_root.current = workInProgress;
// TODO: We can be smarter here and only look for more work in the
// "next" scheduled work since we've already scanned passed. That
// also ensures that work scheduled during reconciliation gets deferred.
// const hasMoreWork = workInProgress.pendingWorkPriority !== NoWork;
commitAllWork(workInProgress);
var nextWork = findNextUnitOfWork();
// if (!nextWork && hasMoreWork) {
// TODO: This can happen when some deep work completes and we don't
// know if this was the last one. We should be able to keep track of
// the highest priority still in the tree for one pass. But if we
// terminate an update we don't know.
// throw new Error('FiberRoots should not have flagged more work if there is none.');
// }
return nextWork;
}
}
}
function performUnitOfWork(workInProgress) {
// The current, flushed, state of this fiber is the alternate.
// Ideally nothing should rely on this, but relying on it here
// means that we don't need an additional field on the work in
// progress.
var current = workInProgress.alternate;
var next = beginWork(current, workInProgress, nextPriorityLevel);
if (next) {
// If this spawns new work, do that next.
return next;
} else {
// Otherwise, complete the current work.
return completeUnitOfWork(workInProgress);
}
}
function performDeferredWork(deadline) {
if (!nextUnitOfWork) {
nextUnitOfWork = findNextUnitOfWork();
}
while (nextUnitOfWork) {
if (deadline.timeRemaining() > timeHeuristicForUnitOfWork) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
if (!nextUnitOfWork) {
// Find more work. We might have time to complete some more.
nextUnitOfWork = findNextUnitOfWork();
}
} else {
scheduleDeferredCallback(performDeferredWork);
return;
}
}
}
function scheduleDeferredWork(root, priority) {
// 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, 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;
}
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;
scheduleAnimationCallback(performAnimationWork);
}
}
function scheduleWork(root) {
if (defaultPriority === SynchronousPriority) {
throw new Error('Not implemented yet');
}
if (defaultPriority === NoWork) {
return;
}
if (defaultPriority > AnimationPriority) {
scheduleDeferredWork(root, defaultPriority);
return;
}
scheduleAnimationWork(root, defaultPriority);
}
function performWithPriority(priorityLevel, fn) {
var previousDefaultPriority = defaultPriority;
defaultPriority = priorityLevel;
try {
fn();
} finally {
defaultPriority = previousDefaultPriority;
}
}
scheduler = {
scheduleWork: scheduleWork,
scheduleDeferredWork: scheduleDeferredWork,
performWithPriority: performWithPriority
};
return scheduler;
};