Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

performSyncWork 和 perfromAsyncWork #5

Open
lz-lee opened this issue Aug 5, 2019 · 0 comments
Open

performSyncWork 和 perfromAsyncWork #5

lz-lee opened this issue Aug 5, 2019 · 0 comments

Comments

@lz-lee
Copy link
Owner

lz-lee commented Aug 5, 2019

performSyncWork 和 perfromAsyncWork

  • 都是调用的 performWork
  • 异步方式设置的 minExpirationTimeNoWork 0,通过判断 dl.didTimeout 是否过期,如果过期则设置 root.newExpirationTimeToWorkOn 为当前时间,表示这个任务可以直接执行,不需要判断是否超过帧时间
  • 同步方式设置的 minExpirationTimeSync 1

performAsyncWork

function performAsyncWork(dl) {
  // didTimeout 为 true 任务过期,立即执行
  if (dl.didTimeout) {
    if (firstScheduledRoot !== null) {
      // 设置当前渲染时间为当前时间
      recomputeCurrentRendererTime();
      let root: FiberRoot = firstScheduledRoot;
      do {
        // 标记 root 节点变量, 如果当前任务过期设置root.newExpirationTimeToWorkOn 为当前时间
        didExpireAtExpirationTime(root, currentRendererTime);
        // 找 下一个 root
        root = (root.nextScheduledRoot: any);
      } while (root !== firstScheduledRoot);
    }
  }
  performWork(NoWork, dl);
}

function performSyncWork() {
  performWork(Sync, null);
}

function didExpireAtExpirationTime(
  root: FiberRoot,
  currentTime: ExpirationTime,
): void {
  const expirationTime = root.expirationTime;
  // root 已经过期
  if (expirationTime !== NoWork && currentTime >= expirationTime) {
    // The root has expired. Flush all work up to the current time.
    //
    root.nextExpirationTimeToWorkOn = currentTime;
  }
}

performWork

  • 是否有 deadline 的区分异步/同步,用来判断一帧的渲染时间内还有没有时间留给 React 更新的

  • 循环渲染 Root 的条件:循环不同 root 和不同优先级任务来更新

  • 超过时间片的处理:deadline 这一帧的时间到了把执行权交回浏览器

  • shouldYield 方法用来判断一帧渲染时间内是否还有剩余时间留个React更新,改变全局变量 deadlineDidExpire,为 false 表示还有时间留给React更新

function shouldYield() {
  if (deadlineDidExpire) {
    return true;
  }
  // deadline.timeRemaining 返回的值表示一帧渲染时间还有剩余, 直接return false, 此时deadlineDidExpire 为false
  if (
    deadline === null ||
    deadline.timeRemaining() > timeHeuristicForUnitOfWork
  ) {
    // Disregard deadline.didTimeout. Only expired work should be flushed
    // during a timeout. This path is only hit for non-expired work.
    return false;
  }
  deadlineDidExpire = true;
  return true;
}
function performWork(minExpirationTime: ExpirationTime, dl: Deadline | null) {
  deadline = dl;

  // Keep working on roots until there's no more work, or until we reach
  // the deadline.
  findHighestPriorityRoot();

  // 异步
  if (deadline !== null) {
    // 重新计算当前渲染时间
    recomputeCurrentRendererTime();
    currentSchedulerTime = currentRendererTime;

    while (
      nextFlushedRoot !== null &&
      nextFlushedExpirationTime !== NoWork &&
      (minExpirationTime === NoWork ||
        minExpirationTime >= nextFlushedExpirationTime) &&
        // 还有剩余时间更新 或者 任务已经过期需要强制输出
      (!deadlineDidExpire || currentRendererTime >= nextFlushedExpirationTime)
    ) {
      performWorkOnRoot(
        nextFlushedRoot,
        nextFlushedExpirationTime,
        // 过期为 true,未过期为 false
        currentRendererTime >= nextFlushedExpirationTime,
      );
      findHighestPriorityRoot();
      recomputeCurrentRendererTime();
      currentSchedulerTime = currentRendererTime;
    }
  } else {
    // 同步
    while (
      // root 非空
      nextFlushedRoot !== null &&
      // root 有更新
      nextFlushedExpirationTime !== NoWork &&
      // minExpirationTime 为 Sync,那么就只能是处理 nextFlushedExpirationTime 为Sync的情况,即同步执行的情况
      (minExpirationTime === NoWork ||
        minExpirationTime >= nextFlushedExpirationTime)
    ) {
      performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, true);
      findHighestPriorityRoot();
    }
  }

  // We're done flushing work. Either we ran out of time in this callback,
  // or there's no more work left with sufficient priority.

  // If we're inside a callback, set this to false since we just completed it.
  if (deadline !== null) {
    callbackExpirationTime = NoWork;
    callbackID = null;
  }
  // If there's work left over, schedule a new callback.
  if (nextFlushedExpirationTime !== NoWork) {
    scheduleCallbackWithExpirationTime(
      ((nextFlushedRoot: any): FiberRoot),
      nextFlushedExpirationTime,
    );
  }

  // Clean-up.
  deadline = null;
  deadlineDidExpire = false;

  finishRendering();
}

findHighestPriorityRoot

  • 找到更新优先级最高的root
  • 并将 root 赋值给 nextFlushedRoot, root对应的 expirationTime 赋值给 nextFlushedExpirationTime
  • 默认只有一个 root 的情况下
firstScheduledRoot = lastScheduledRoot = root;
root.nextScheduledRoot = root;
function findHighestPriorityRoot() {
  let highestPriorityWork = NoWork;
  let highestPriorityRoot = null;
  // 有 root 的情况
  if (lastScheduledRoot !== null) {
    let previousScheduledRoot = lastScheduledRoot;
    let root = firstScheduledRoot;
    while (root !== null) {
      const remainingExpirationTime = root.expirationTime;
      // Nowork 表示节点没有更新
      if (remainingExpirationTime === NoWork) {
        // This root no longer has work. Remove it from the scheduler.
        // 只有一个 root 的情况
        if (root === root.nextScheduledRoot) {
          // This is the only root in the list.
          root.nextScheduledRoot = null;
          firstScheduledRoot = lastScheduledRoot = null;
          break;
        }
        // ....
      } else {
        // root 上有更新
        if (
          highestPriorityWork === NoWork ||
          remainingExpirationTime < highestPriorityWork
        ) {
          // Update the priority, if it's higher
          // 找更新优先级最高的 root 和它对应的 expirationTime
          highestPriorityWork = remainingExpirationTime;
          highestPriorityRoot = root;
        }
        if (root === lastScheduledRoot) {
          break;
        }
        if (highestPriorityWork === Sync) {
          // Sync is highest priority by definition so
          // we can stop searching.
          break;
        }
        previousScheduledRoot = root;
        root = root.nextScheduledRoot;
      }
    }
  }

  nextFlushedRoot = highestPriorityRoot;
  nextFlushedExpirationTime = highestPriorityWork;
}

performWorkonRoot

  • isRendering 置为 true,执行完后为 false
  • renderRoot 渲染阶段
  • completeRoot 提交(commit)阶段
    - finishedWork 是已经完成 renderRoot 渲染阶段的任务,只有 renderRoot 后才不为 null
  • 异步情况可以被打断,如果还有剩余时间更新,那么继续走 completeRoot 提交。没有剩余时间则等到下个时间段进入 performWorkOnRoot 判断
function performWorkOnRoot(
  root: FiberRoot,
  expirationTime: ExpirationTime,
  isExpired: boolean,
) {
  isRendering = true;

  // Check if this is async work or sync/expired work.
  // 同步任务 或者 过期任务
  if (deadline === null || isExpired) {
    // Flush work without yielding.
    // TODO: Non-yieldy work does not necessarily imply expired work. A renderer
    // may want to perform some work without yielding, but also without
    // requiring the root to complete (by triggering placeholders).

    let finishedWork = root.finishedWork;
    // 上一次 renderRoot 完,但没有时间 completeRoot 了
    if (finishedWork !== null) {
      // This root is already complete. We can commit it.
      completeRoot(root, finishedWork, expirationTime);
    } else {
      root.finishedWork = null;
      // If this root previously suspended, clear its existing timeout, since
      // we're about to try rendering again.
      // suspense 功能相关
      const timeoutHandle = root.timeoutHandle;
      if (timeoutHandle !== noTimeout) {
        root.timeoutHandle = noTimeout;
        // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
        cancelTimeout(timeoutHandle);
      }
      // 表示这个任务不可中断(同步或过期任务不可中断)
      const isYieldy = false;
     // renderRoot 之后赋值 finishedWork
      renderRoot(root, isYieldy, isExpired);
      finishedWork = root.finishedWork;
      if (finishedWork !== null) {
        // We've completed the root. Commit it.
        completeRoot(root, finishedWork, expirationTime);
      }
    }
  } else {
    // Flush async work.
    let finishedWork = root.finishedWork;
    if (finishedWork !== null) {
      // This root is already complete. We can commit it.
      completeRoot(root, finishedWork, expirationTime);
    } else {
      root.finishedWork = null;
      // If this root previously suspended, clear its existing timeout, since
      // we're about to try rendering again.
      const timeoutHandle = root.timeoutHandle;
      if (timeoutHandle !== noTimeout) {
        root.timeoutHandle = noTimeout;
        // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
        cancelTimeout(timeoutHandle);
      }
      // 异步任务是可以中断的
      const isYieldy = true;
     // renderRoot 之后赋值 finishedWork
      renderRoot(root, isYieldy, isExpired);
      finishedWork = root.finishedWork;
      // 可能被中断,被中断 finishedWork 为null
      if (finishedWork !== null) {
        // We've completed the root. Check the deadline one more time
        // before committing.
        // 还有剩余时间更新则继续 completeRoot
        if (!shouldYield()) {
          // Still time left. Commit the root.
          completeRoot(root, finishedWork, expirationTime);
        } else {
          // 没有时间更新了, 待到下个时间段进入 performWorkOnRoot 判断
          // There's no time left. Mark this root as complete. We'll come
          // back and commit it later.
          root.finishedWork = finishedWork;
        }
      }
    }
  }

  isRendering = false;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant