Cosen95 / blog

关注行业前沿,分享所见所学。持续输出优质文章 :rocket:
212 stars 15 forks source link

requestWork #58

Open Cosen95 opened 4 years ago

Cosen95 commented 4 years ago

requestWork

首先说明以下requestWork的核心功能:

下面,我们来详细分析下requestWork的流程。首先来看requestWork

// requestWork is called by the scheduler whenever a root receives an update.
// It's up to the renderer to call renderRoot at some point in the future.
function requestWork(root: FiberRoot, expirationTime: ExpirationTime) {
  // 把当前 root设置为最高优先级
  addRootToSchedule(root, expirationTime);
  if (isRendering) {
    // 在render过程当中, 此时直接return
    // Prevent reentrancy. Remaining work will be scheduled at the end of
    // the currently rendering batch.
    return;
  }

  // 批量处理相关
  // 调用 setState 时在 enqueueUpdates 前 batchedUpdates 会把 isBatchingUpdates 设置成 true
  if (isBatchingUpdates) {
    // Flush work at the end of the batch.
    if (isUnbatchingUpdates) {
      // ...unless we're inside unbatchedUpdates, in which case we should
      // flush it now.
      nextFlushedRoot = root;
      nextFlushedExpirationTime = Sync;
      performWorkOnRoot(root, Sync, false);
    }
    return;
  }

  // TODO: Get rid of Sync and use current time?
  if (expirationTime === Sync) {
    // 同步的调用 js 代码
    performSyncWork();
  } else {
    // 异步调度 独立的 react 模块包,利用浏览器有空闲的时候进行执行,设置 deadline 在此之前执行
    scheduleCallbackWithExpirationTime(root, expirationTime);
  }
}

首先调用了addRootToSchedule

/**
 * 将 root 加入到调度队列
 *
 * @param {FiberRoot} root
 * @param {ExpirationTime} expirationTime
 */
function addRootToSchedule(root: FiberRoot, expirationTime: ExpirationTime) {
  // Add the root to the schedule.
  // Check if this root is already part of the schedule.

  // root.nextScheduledRoot 用来判断是否有异步任务正在调度, 为 null 时会增加 nextScheduledRoot
  // 这个 root 还没有进入过调度
  if (root.nextScheduledRoot === null) {
    // This root is not already scheduled. Add it.

    root.expirationTime = expirationTime;
    if (lastScheduledRoot === null) {
      // lastScheduledRoot firstScheduledRoot 是单向链表结构,表示多个 root 更新
      // 这里只有一个 root 只会在这里执行
      firstScheduledRoot = lastScheduledRoot = root;
      root.nextScheduledRoot = root;
    } else {
      // 有个多个root 时进行单向链表的插入操作
      lastScheduledRoot.nextScheduledRoot = root;
      lastScheduledRoot = root;
      lastScheduledRoot.nextScheduledRoot = firstScheduledRoot;
    }
  } else {
    // This root is already scheduled, but its priority may have increased.
    // 传入的 root 已经进入过调度, 把 root 的优先级设置最高
    const remainingExpirationTime = root.expirationTime;
    // 如果 root 的 expirationTime 是同步或者优先级低,增加为计算出的最高优先级
    if (expirationTime > remainingExpirationTime) {
      // Update the priority.
      // 把当前 root 的优先级设置为当前优先级最高的
      root.expirationTime = expirationTime;
    }
  }
}

作用比较清晰:

回到requestWork,分成了三个分支:

我们来分别解释一下:

isRendering 顾名思义在render过程当中, 此时直接返回return

batchedUpdate批处理的时候,将isRendering置成true,这样每次在执行setState时,都会走isRendering的分支直接返回,阻止更新,相当于暂停住,当第二个setState时依旧直接返回,避免出现频繁式的更新,也就是批处理。

isBatchingUpdates中又有一个isUnbatchingUpdates分支。isUnbatchingUpdates会在执行unbatchedUpdates()时设置为true。 也就是在首次渲染的时候不需要进行批处理,所以就会进行立即更新。 这里的两个变量名可能会造成误解,isBatchingUpdatesisUnbatchingUpdates这两个变量并不是互斥的,而是在两个方法中设置的哨兵变量。

如果是批处理的过程中的话,也会直接return

当前面的expirationTime被设置成了同步任务的时候,就会立即执行。 剩余的则会进入scheduleCallbackWithExpirationTime 通过ExpirationTime 进行调度。