不同的 expirationTime
1 )概述
- 在React中不仅仅有异步任务
- 大部分情况下都是同步的任务,所以会有不同 expirationTime 的存在
2 )种类
- A. Sync 模式,优先级最高 - 任务创建完成之后,立马更新到真正的dom里面
- 是一个创建即更新的流程
 
- B. Async 模式, 异步模式 - 会有一个调度
- 包含一系列复杂的操作在里面
- 可能会中断,所以会有一个计算出来的过期时间
- 过期时间根据异步的两种情况,如前文所述 - 最低优先级的异步
- 最高优先级的异步
 
 
- C. 指定context
3 ) 源码
- 在 ReactFiberReconciler.js 中的 updateContainer中- 调用的是 computeExpirationForFiber- 它接收两个参数: currentTime: ExpirationTime, fiber: Fiber
 
- 这个方法来自于 ReactFiberScheduler.js 文件function computeExpirationForFiber(currentTime: ExpirationTime, fiber: Fiber) {let expirationTime;if (expirationContext !== NoWork) {// An explicit expiration context was set;expirationTime = expirationContext;} else if (isWorking) {if (isCommitting) {// Updates that occur during the commit phase should have sync priority// by default.expirationTime = Sync;} else {// Updates during the render phase should expire at the same time as// the work that is being rendered.expirationTime = nextRenderExpirationTime;}} else {// No explicit expiration context was set, and we're not currently// performing work. Calculate a new expiration time.if (fiber.mode & ConcurrentMode) {if (isBatchingInteractiveUpdates) {// This is an interactive updateexpirationTime = computeInteractiveExpiration(currentTime);} else {// This is an async updateexpirationTime = computeAsyncExpiration(currentTime);}// If we're in the middle of rendering a tree, do not update at the same// expiration time that is already rendering.if (nextRoot !== null && expirationTime === nextRenderExpirationTime) {expirationTime += 1;}} else {// This is a sync updateexpirationTime = Sync;}}if (isBatchingInteractiveUpdates) {// This is an interactive update. Keep track of the lowest pending// interactive expiration time. This allows us to synchronously flush// all interactive updates when needed.if (expirationTime > lowestPriorityPendingInteractiveExpirationTime) {lowestPriorityPendingInteractiveExpirationTime = expirationTime;}}return expirationTime; }- 1 )这里有一系列的判断,首先是对 expirationContext的判断- 它是 ExpirationTime类型,默认初始化值是NoWork
- 下面是它的一个修改场景function deferredUpdates<A>(fn: () => A): A {const currentTime = requestCurrentTime();const previousExpirationContext = expirationContext;const previousIsBatchingInteractiveUpdates = isBatchingInteractiveUpdates;expirationContext = computeAsyncExpiration(currentTime); // 这里isBatchingInteractiveUpdates = false;try {return fn();} finally {expirationContext = previousExpirationContext;isBatchingInteractiveUpdates = previousIsBatchingInteractiveUpdates;} }- expirationContext = computeAsyncExpiration(currentTime);
- 可以看到 computeAsyncExpiration这个函数对其进行修改赋值
 
- 还有一种场景, 往下进行搜索function syncUpdates<A, B, C0, D, R>(fn: (A, B, C0, D) => R,a: A,b: B,c: C0,d: D, ): R {const previousExpirationContext = expirationContext;expirationContext = Sync; // 变成了 Synctry {return fn(a, b, c, d); // 这里是 setState 操作} finally {expirationContext = previousExpirationContext; // 最终还原} }- syncUpdates在 ReactDOM.js 中的- flushSyncAPI- flushSync: DOMRenderer.flushSync一直溯源往上找到 ReactFiberScheduler.js 中的- flushSync
- 这个就是 本源的 flushSync// TODO: Batching should be implemented at the renderer level, not within // the reconciler. function flushSync<A, R>(fn: (a: A) => R, a: A): R {invariant(!isRendering,'flushSync was called from inside a lifecycle method. It cannot be ' +'called when React is already rendering.',);const previousIsBatchingUpdates = isBatchingUpdates;isBatchingUpdates = true;try {return syncUpdates(fn, a); // 这里} finally {isBatchingUpdates = previousIsBatchingUpdates;performSyncWork();} }
- 上述调用的是 syncUpdates,在之前的示例中,如下flushSync(() => {this.setState({num: newNum,}) })
- 上述示例就是传入 fn 回调, 内部调用 setState
- 在这种场景下 expirationTime变成了Sync
 
 
- 以上两种情况 是给 expirationContext进行赋值
 
- 它是 
- 2 )当 expirationTime变成了Sync就不符合第一种情况了,这时候往下走,匹配到了isWorking- isWorking表示有任务正在更新
- 也是基于条件指定某个值
- 这块涉及到后续任务的更新,跳过
 
- 3 )没有外部强制的情况下 - if (fiber.mode & ConcurrentMode)
- 判断 fiber.mode是否是ConcurrentMode
- 找到 ConcurrentMode的定义处 ReactTypeOfMode.jsexport type TypeOfMode = number;export const NoContext = 0b000; export const ConcurrentMode = 0b001; export const StrictMode = 0b010; export const ProfileMode = 0b100;
- 可见,使用二进制方式来定义的
- 可以通过 与或 这类逻辑的操作,非常方便的组合 Mode
- 以及判断是否有某个Mode,例如const a = 0b000; const b = 0b001; const c = 0b010; const d = 0b100; let mode = a; // 默认等于 a, 在后续渲染时并不知道是否有更改过 mode & b // 如果 结果为 0 表示没有过b这种情况 mode |= b // 这样给 mode 增加 b,这时候 mode 变成1,就对应了 b mode |= c // 给 mode 增加 c, 这时候 mode 变成 3,也就是 0b011 mode & b // 这时候判断mode是否有b, 如果是1,则有b, 结果是1 对应b mode & c // 这时候判断mode是否有c, 如果是1,则有c, 结果是2 对应c- 这是一种巧妙的设计方式
- 类似的,在 ReactSideEffectTags.js 中也是这么设计的
 
- 回到代码里,如果是 ConcurrentMode 才会调用
- computeInteractiveExpiration和- computeAsyncExpiration两个函数中的一个计算- expirationTime- if (isBatchingInteractiveUpdates) {// This is an interactive updateexpirationTime = computeInteractiveExpiration(currentTime); } else {// This is an async updateexpirationTime = computeAsyncExpiration(currentTime); }- 关于 isBatchingInteractiveUpdates在interactiveUpdates函数中被赋值为 true- 大部分的 React 事件产生的更新,事件中绑定回调函数,这个回调函数执行的时候
- 大部分都是在 interactiveUpdates情况下,也就是isBatchingInteractiveUpdates为 true 时
 
- 后面 expirationTime += 1;是为了区分下一个即将进行的更新和当前正在创造的更新,防止一样,强制把当前+1
 
- 关于 
- 如果不属于,则还是 Sync
 
 
- 1 )这里有一系列的判断,首先是对 
 
- 调用的是