国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

React Fiber源碼分析 第三篇(異步狀態(tài))

worldligang / 1337人閱讀

摘要:系列文章源碼分析第一篇源碼分析第二篇同步模式源碼分析第三篇異步狀態(tài)源碼分析第四篇歸納總結(jié)前言是在版本中的大更新,利用了閑余時間看了一些源碼,做個小記錄流程圖源碼分析調(diào)用時,會調(diào)用的方法,同時將新的作為參數(shù)傳進會先調(diào)用獲取一個維護兩個時間一個

系列文章

React Fiber源碼分析 第一篇
React Fiber源碼分析 第二篇(同步模式)
React Fiber源碼分析 第三篇(異步狀態(tài))
React Fiber源碼分析 第四篇(歸納總結(jié))

前言

React Fiber是React在V16版本中的大更新,利用了閑余時間看了一些源碼,做個小記錄~

流程圖

源碼分析

1.調(diào)用setState時, 會調(diào)用classComponentUpdaterenqueueSetState方法, 同時將新的state作為payload參數(shù)傳進enqueueSetState會先調(diào)用requestCurrentTime獲取一個currentTime

function requestCurrentTime() {
  // 維護兩個時間 一個renderingTime 一個currentSechedulerTime
 //  rederingTime 可以隨時更新  currentSechedulerTime只有在沒有新任務(wù)的時候才更新
  if (isRendering) {
    return currentSchedulerTime;
  }
  findHighestPriorityRoot();
  if (nextFlushedExpirationTime === NoWork || nextFlushedExpirationTime === Never) {
    recomputeCurrentRendererTime();
    currentSchedulerTime = currentRendererTime;
    return currentSchedulerTime;
  }
  return currentSheculerTime

2.通過獲取到的currentTime, 調(diào)用computeExpirationForFiber,計算該fiber的優(yōu)先級

if (fiber.mode & AsyncMode) {
      if (isBatchingInteractiveUpdates) {
        // This is an interactive update
        expirationTime = computeInteractiveExpiration(currentTime);
      } else {
        // This is an async update
        expirationTime = computeAsyncExpiration(currentTime);
      }
      ...
    }

3.這個函數(shù)其他點比較簡單, 里面主要有下面 這個判斷要說明一下, 如果是屬于異步更新的話,會根據(jù)是 交互引起的更新 還是其他更新 來調(diào)用不同的函數(shù)computeInteractiveExpiration和computeAsyncExpiration

可以看到這兩個函數(shù)最后返回的都是computeExpirationBucket函數(shù)的結(jié)果, 只是入?yún)⒉煌?computeInteractiveExpiration的參數(shù)是500, 100, computeAsyncExpiration的參數(shù)是5000, 250, 然后看computeExpirationBucket函數(shù)可以看到, 第二個參數(shù)(500和5000)越大,則返回的expirationTime越大, 也就是說 computeInteractiveExpiration的更新優(yōu)先級高于computeAsyncExpiration, 則交互的優(yōu)先級高于其他

獲得優(yōu)先級后則和同步更新一樣, 創(chuàng)建update并放進隊列, 然后調(diào)用sheuduleWork

var classComponentUpdater = {
  isMounted: isMounted,
  enqueueSetState: function (inst, payload, callback) {
    var fiber = get(inst);
   // 獲得優(yōu)先級
    var currentTime = requestCurrentTime();
    var expirationTime = computeExpirationForFiber(currentTime, fiber);
   // 創(chuàng)建更新
    var update = createUpdate(expirationTime);
    update.payload = payload;
    if (callback !== undefined && callback !== null) {
      update.callback = callback;
    }

    enqueueUpdate(fiber, update);
    scheduleWork(fiber, expirationTime);
  },

4.接下來的步驟和同步一樣, 直到同步調(diào)用的是performSyncWork函數(shù), 而異步調(diào)用的是scheduleCallbackWithExpirationTime函數(shù)

scheduleCallbackWithExpirationTime函數(shù)首先判斷是否存在callback正在進行中, 判斷現(xiàn)有expirationTime和其優(yōu)先級,若優(yōu)先級比較低則直接返回, 否則設(shè)置現(xiàn)在的fiber任務(wù)為新的callback,并把原來的回調(diào)從列表中移除

function scheduleCallbackWithExpirationTime(root, expirationTime) {
  if (callbackExpirationTime !== NoWork) {
    //  判斷優(yōu)先級
    if (expirationTime > callbackExpirationTime) {
      // Existing callback has sufficient timeout. Exit.
      return;
    } else {
      if (callbackID !== null) {
        // 取消, 從回調(diào)列表中刪除
        schedule.unstable_cancelScheduledWork(callbackID);
      }
    }
    // The request callback timer is already running. Don"t start a new one.
  } 
  // 設(shè)置新的callback和callbackExiporationTime
  callbackExpirationTime = expirationTime;
  var currentMs = schedule.unstable_now() - originalStartTimeMs;
  var expirationTimeMs = expirationTimeToMs(expirationTime);
  // 計算是否超時 
  var timeout = expirationTimeMs - currentMs;
  callbackID = schedule.unstable_scheduleWork(performAsyncWork, { timeout: timeout });
}

5.接下來調(diào)用schedule.unstable_scheduleWork(performAsyncWork, { timeout: timeout })函數(shù), 并生成一個節(jié)點, 存儲回調(diào)函數(shù)和超時時間,插入到回調(diào)列表, 并根據(jù)超時排序, 調(diào)用ensureHostCallBackIsScheduled函數(shù),最后返回該節(jié)點

function unstable_scheduleWork(callback, options) {
  var currentTime = exports.unstable_now();

  var timesOutAt; 
  // 獲取超時時間
  if (options !== undefined && options !== null && options.timeout !== null && options.timeout !== undefined) {
    // Check for an explicit timeout 
    timesOutAt = currentTime + options.timeout;
  } else {
    // Compute an absolute timeout using the default constant.
    timesOutAt = currentTime + DEFERRED_TIMEOUT;
  }
 // 生成一個節(jié)點, 存儲回調(diào)函數(shù)和超時時間
  var newNode = {
    callback: callback,
    timesOutAt: timesOutAt,
    next: null,
    previous: null
  };

  // 插入到回調(diào)列表, 并根據(jù)超時排序, 最后返回該節(jié)點
  if (firstCallbackNode === null) {
    // This is the first callback in the list.
    firstCallbackNode = newNode.next = newNode.previous = newNode;
    ensureHostCallbackIsScheduled(firstCallbackNode);
  } else {
    ...var previous = next.previous;
    previous.next = next.previous = newNode;
    newNode.next = next;
    newNode.previous = previous;
  }

  return newNode;
}

6.ensureHostCallBackIsScheduled函數(shù)如名, 相對比較簡單

function ensureHostCallbackIsScheduled() {
  if (isPerformingWork) {
    // Don"t schedule work yet; wait until the next time we yield.
    return;
  }
  // Schedule the host callback using the earliest timeout in the list.
  var timesOutAt = firstCallbackNode.timesOutAt;
  if (!isHostCallbackScheduled) {
    isHostCallbackScheduled = true;
  } else {
    // Cancel the existing host callback.
    cancelCallback();
  }
  requestCallback(flushWork, timesOutAt);
}

7.往下看requestCallback, 這里說的如果已經(jīng)在執(zhí)行任務(wù)的話, 就必須有一個錯誤被拋出(拋出的錯誤是啥??),同時不要等待下一幀, 盡快開始新事件

如果如果當(dāng)前沒有調(diào)度幀回調(diào)函數(shù),我們需要進行一個調(diào)度幀回調(diào)函數(shù), 并設(shè)置isAnimationFrameScheduledtrue,
接著執(zhí)行requestAnimationFrameWithTimeout;函數(shù)

requestCallback = function (callback, absoluteTimeout) {
    scheduledCallback = callback;
    timeoutTime = absoluteTimeout;
    if (isPerformingIdleWork) {
      // 如果已經(jīng)在執(zhí)行任務(wù)的話, 就必須有一個錯誤被拋出(拋出的錯誤是啥??),同時不要等待下一幀, 盡快開始新事件
      window.postMessage(messageKey, "*");
    } else if (!isAnimationFrameScheduled) {
      isAnimationFrameScheduled = true;
      requestAnimationFrameWithTimeout(animationTick);
    }
  };

8.requestAnimationFrameWithTimeout函數(shù)就是執(zhí)行一個異步操作, 執(zhí)行完畢后, 假設(shè)此時又有N個回調(diào)任務(wù)進入, 同時原來的回調(diào)還沒有進行, 則回到scheduleCallbackWithExpirationTime函數(shù)上,

分為兩個分支:

假設(shè)優(yōu)先級低于目前的回調(diào)任務(wù), 則直接返回(已經(jīng)把root加到root隊列中)

優(yōu)先級高于目前的回調(diào)任務(wù), 將目前的回調(diào)任務(wù)從列表中移除, 并將callBackID設(shè)為傳入的回調(diào), 接下來的路線與上面一致, 假設(shè)該傳入的回調(diào)超時最早, 則會進入到cancelCallback函數(shù),重置各變量, 并進入到requestCallback函數(shù), 此時除了賦值操作,沒有其他動作

到了這時候, 已經(jīng)把新的回調(diào)替換正在進行的回調(diào)到回調(diào)列表。
函數(shù)正常執(zhí)行, 調(diào)用callback, 即animationTick函數(shù)

cancelCallback = function () {
    scheduledCallback = null;
    isIdleScheduled = false;
    timeoutTime = -1;
  };
var ANIMATION_FRAME_TIMEOUT = 100;
var rAFID;
var rAFTimeoutID;
var requestAnimationFrameWithTimeout = function (callback) {
  // schedule rAF and also a setTimeout
  rAFID = localRequestAnimationFrame(function (timestamp) {
    // cancel the setTimeout
    localClearTimeout(rAFTimeoutID);
    callback(timestamp);
  });
  rAFTimeoutID = localSetTimeout(function () {
    // cancel the requestAnimationFrame
    localCancelAnimationFrame(rAFID);
    callback(exports.unstable_now());
  }, ANIMATION_FRAME_TIMEOUT);
};

9.animationTick一個是把isAnimationFrameScheduled狀態(tài)設(shè)為false, 即不在調(diào)度幀回調(diào)的狀態(tài), 同時計算幀到期時間frameDeadline , 判斷是否在幀回調(diào)的狀態(tài), 否的話調(diào)用window.postMessage ,并設(shè)置isIdleScheduled狀態(tài)為true

假設(shè)此時, 有N個回調(diào)進入, 分為兩個情況:
1.假設(shè)優(yōu)先級低于目前的回調(diào)任務(wù), 則直接返回(已經(jīng)把root加到root隊列中)
2.優(yōu)先級高于目前的回調(diào)任務(wù), 將目前的回調(diào)任務(wù)從列表中移除, 并將callBackID設(shè)為傳入的回調(diào), 接下來的路線與上面一致,一直到animationTick函數(shù),因為 postMessagesetTImeout更快執(zhí)行,所以此時isIdleScheduledfalse,和之前一樣正常執(zhí)行。

var animationTick = function (rafTime) {
    isAnimationFrameScheduled = false;
    ...
    ...
    // 每幀到期時間為33ms
    frameDeadline = rafTime + activeFrameTime;
    if (!isIdleScheduled) {
      isIdleScheduled = true;
      window.postMessage(messageKey, "*");
    }
  };

10.postMessage會執(zhí)行idleTick , 首先把isIdleScheduleddidTimeout置為false,

先判斷幀到期時間和超時時間是否小于當(dāng)前時間, 如果是的話, 則置didTimeout為true,
如果幀到期, 但超時時間小于當(dāng)前時間, 則置isAnimationFrameScheduled 為false, 并調(diào)用requestAnimationFrameWithTimeout, 即進入下一幀
如果幀未到期, 則調(diào)用callbak函數(shù), 并把isPerformingIdleWork置為true

idleTick 會先執(zhí)行callback, 完成后才將isPerformingIdleWork 置為false, 執(zhí)行callback的時候會傳入didTimeout作為參數(shù), callback為flushWork

var idleTick = function (event) {
    ...
    isIdleScheduled = false;

    var currentTime = exports.unstable_now();

    var didTimeout = false;
    if (frameDeadline - currentTime <= 0) {
      // 幀過期
      if (timeoutTime !== -1 && timeoutTime <= currentTime) {
        // 回調(diào)超時
        didTimeout = true;
      } else {
        // No timeout.
        if (!isAnimationFrameScheduled) {
          // 到下一幀繼續(xù)任務(wù)
          isAnimationFrameScheduled = true;
          requestAnimationFrameWithTimeout(animationTick);
        }
        // Exit without invoking the callback.
        return;
      }
    }

    timeoutTime = -1;
    var callback = scheduledCallback;
    scheduledCallback = null;
    if (callback !== null) {
      isPerformingIdleWork = true;
      try {
        callback(didTimeout);
      } finally {
        isPerformingIdleWork = false;
      }
    }
  };

11.flushwork首先把isPerformingWork置為true, 然后把didTimeout賦值給deallinObject對象, 接下來進行判斷
如果已經(jīng)過了幀的結(jié)束期, 則判斷鏈表中有哪個節(jié)點已超時, 并循環(huán)調(diào)用flushFirstCallback函數(shù)解決超時節(jié)點,
如果還沒有過幀的結(jié)束期, 則調(diào)用flushFirstCallback函數(shù)處理鏈表中的第一個節(jié)點, 循環(huán)處理一直到該幀結(jié)束

最后, flushwork函數(shù)會將isPerformingWork置為false, 并判斷是否還有任務(wù) 有則執(zhí)行ensureHostCallbackIsScheduled函數(shù)

function flushWork(didTimeout) {
  isPerformingWork = true;
  deadlineObject.didTimeout = didTimeout;
  try {
    if (didTimeout) {
      while (firstCallbackNode !== null) {
        var currentTime = exports.unstable_now();
        if (firstCallbackNode.timesOutAt <= currentTime) {
          do {
            flushFirstCallback();
          } while (firstCallbackNode !== null && firstCallbackNode.timesOutAt <= currentTime);
          continue;
        }
        break;
      }
    } else {
      // Keep flushing callbacks until we run out of time in the frame.
      if (firstCallbackNode !== null) {
        do {
          flushFirstCallback();
        } while (firstCallbackNode !== null && getFrameDeadline() - exports.unstable_now() > 0);
      }
    }
  } finally {
    isPerformingWork = false;
    if (firstCallbackNode !== null) {
      // There"s still work remaining. Request another callback.
      ensureHostCallbackIsScheduled(firstCallbackNode);
    } else {
      isHostCallbackScheduled = false;
    }
  }
}

12.繼續(xù)往下看, 則是flushFirstCallback函數(shù),先把該節(jié)點從鏈表中清掉, 然后調(diào)用callback函數(shù), 并帶入deadlineObject作為參數(shù)

function flushFirstCallback(node) {
  var flushedNode = firstCallbackNode;

  //從鏈表中清理掉該節(jié)點, 這樣哪怕出錯了, 也能保留原鏈表狀態(tài)
  var next = firstCallbackNode.next;
  if (firstCallbackNode === next) {
    // This is the last callback in the list.
    firstCallbackNode = null;
    next = null;
  } else {
    var previous = firstCallbackNode.previous;
    firstCallbackNode = previous.next = next;
    next.previous = previous;
  }

  flushedNode.next = flushedNode.previous = null;

  // Now it"s safe to call the callback.
  var callback = flushedNode.callback;
  callback(deadlineObject);
}

13.接下來的就是performAsyncWork函數(shù),如果didTimeout為true, 則表明至少有一個更新已過期, 迭代所有root任務(wù), 把已過期的root的nextExpirationTimeToWorkOn重置為當(dāng)前時間currentTime.
然后調(diào)用performWork函數(shù)

function performAsyncWork(dl) {
  if (dl.didTimeout) {
    // 刷新所有root的nextEpirationTimeToWorkOn
    if (firstScheduledRoot !== null) {
      recomputeCurrentRendererTime();
      var root = firstScheduledRoot;
      do {
        didExpireAtExpirationTime(root, currentRendererTime);
        // The root schedule is circular, so this is never null.
        root = root.nextScheduledRoot;
      } while (root !== firstScheduledRoot);
    }
  }
  performWork(NoWork, dl);
}

14.performWork函數(shù)在之前已經(jīng)分析過了, 這里主要看存在deadline時的操作, 在幀未到期 或者 當(dāng)前渲染時間大于等于nextFlushedExpirationTime時才執(zhí)行 performWorkOnRoot, 并將currentRendererTime >= nextFlushedExpirationTime作為第三個參數(shù)傳入, 一直循環(huán)處理任務(wù),
最后清除callbackExpirationTime, callBackId, 同時, 如果還有任務(wù)的話, 則繼續(xù)調(diào)用scheduleCallbackWithExpirationTime(nextFlushedRoot, nextFlushedExpirationTime);函數(shù)進入到回調(diào)

function performWork(minExpirationTime, dl) {
  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, currentRendererTime >= nextFlushedExpirationTime);
      findHighestPriorityRoot();
      recomputeCurrentRendererTime();
      currentSchedulerTime = currentRendererTime;
    }
  } 
  if (deadline !== null) {
    callbackExpirationTime = NoWork;
    callbackID = null;
  }
  // If there"s work left over, schedule a new callback.
  if (nextFlushedExpirationTime !== NoWork) {
    scheduleCallbackWithExpirationTime(nextFlushedRoot, nextFlushedExpirationTime);
  }

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

  finishRendering();
}

15.接下來看異步狀態(tài)下的performWorkOnRoot函數(shù)。基本操作和同步一樣, 在進入到renderRoot(root, _isYieldy, isExpired);函數(shù)時, 會根據(jù)是否已超時將isYieldy置為true或者false, 異步狀態(tài)下未超時為false,
renderRoot和同步一樣, 最后執(zhí)行workLoop(isYieldy)
workLoop在未過期的情況下, 會執(zhí)行shouldYield()函數(shù)來判斷是否執(zhí)行nextUnitOfWork, 和同步一樣, 這里只需要關(guān)注shouldYied函數(shù)

function workLoop(isYieldy) {
  if (!isYieldy) {
    // Flush work without yielding
    while (nextUnitOfWork !== null) {
      nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
    }
  } else {
    // Flush asynchronous work until the deadline runs out of time.
    while (nextUnitOfWork !== null && !shouldYield()) {
      nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
    }
  }
}

16.shouldYield函數(shù), 如果deadlineDidExpire為true, 即幀已到期, 直接返回true,
如果deadline不存在, 并且?guī)吹狡冢?則返回false, 可以執(zhí)行單元
否則將deadlineDidExpire置為true

function shouldYield() {
  if (deadlineDidExpire) {
    return true;
  }
  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;
}
總結(jié)

源碼分析到這里就結(jié)束啦,下一篇做一個總結(jié),不然就是流水賬一樣的,容易忘記

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/108977.html

相關(guān)文章

  • React Fiber源碼分析 第一篇

    摘要:系列文章源碼分析第一篇源碼分析第二篇同步模式源碼分析第三篇異步狀態(tài)源碼分析第四篇歸納總結(jié)前言是在版本中的大更新,利用了閑余時間看了一些源碼,做個小記錄流程圖源碼分析先由編譯,調(diào)用,入?yún)椋蛴〕鰜砜梢钥吹剑謩e代表著元素原生元素,回調(diào)函數(shù) 系列文章 React Fiber源碼分析 第一篇 React Fiber源碼分析 第二篇(同步模式) React Fiber源碼分析 第三篇(...

    amc 評論0 收藏0
  • React Fiber源碼分析 第二篇(同步模式)

    摘要:函數(shù)主要執(zhí)行兩個操作,一個是判斷當(dāng)前是否還有任務(wù),如果沒有,則從鏈中移除。 系列文章 React Fiber源碼分析 第一篇 React Fiber源碼分析 第二篇(同步模式) React Fiber源碼分析 第三篇(異步狀態(tài)) React Fiber源碼分析 第四篇(歸納總結(jié)) 前言 React Fiber是React在V16版本中的大更新,利用了閑余時間看了一些源碼,做個小記...

    OBKoro1 評論0 收藏0
  • React Fiber源碼分析 第四篇(歸納總結(jié))

    摘要:為什么網(wǎng)頁性能會變高要回答這個問題,需要回頭看是單線程的知識點。在分析的過程中,發(fā)現(xiàn)了的源碼中使用了很多鏈?zhǔn)浇Y(jié)構(gòu),回調(diào)鏈,任務(wù)鏈等,這個主要是為了增刪時性能比較高 系列文章 React Fiber源碼分析 第一篇 React Fiber源碼分析 第二篇(同步模式) React Fiber源碼分析 第三篇(異步狀態(tài)) React Fiber源碼分析 第四篇(歸納總結(jié)) 前言 Rea...

    jsdt 評論0 收藏0
  • 淺談React Fiber

    摘要:因為版本將真正廢棄這三生命周期到目前為止,的渲染機制遵循同步渲染首次渲染,更新時更新時卸載時期間每個周期函數(shù)各司其職,輸入輸出都是可預(yù)測,一路下來很順暢。通過進一步觀察可以發(fā)現(xiàn),預(yù)廢棄的三個生命周期函數(shù)都發(fā)生在虛擬的構(gòu)建期間,也就是之前。 showImg(https://segmentfault.com/img/bVbweoj?w=559&h=300); 背景 前段時間準(zhǔn)備前端招聘事項...

    izhuhaodev 評論0 收藏0
  • Deep In React之淺談 React Fiber 架構(gòu)(一)

    摘要:在上面我們已經(jīng)知道瀏覽器是一幀一幀執(zhí)行的,在兩個執(zhí)行幀之間,主線程通常會有一小段空閑時間,可以在這個空閑期調(diào)用空閑期回調(diào),執(zhí)行一些任務(wù)。另外由于這些堆棧是可以自己控制的,所以可以加入并發(fā)或者錯誤邊界等功能。 文章首發(fā)于個人博客 前言 2016 年都已經(jīng)透露出來的概念,這都 9102 年了,我才開始寫 Fiber 的文章,表示慚愧呀。不過現(xiàn)在好的是關(guān)于 Fiber 的資料已經(jīng)很豐富了,...

    Jiavan 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<