React源碼4 三大核心模塊之一:Schedule,scheduleUpdateOnFiber函數

scheduler工作階段在React內部被稱為schedule階段。

在《React源碼3》,我們已經將update加入隊列并返回到了根容器節點root。

function updateContainer(element, container, parentComponent, callback) {//前面略過var root = enqueueUpdate(current$1, update, lane);if (root !== null) {scheduleUpdateOnFiber(root, current$1, lane, eventTime);entangleTransitions(root, current$1, lane);}return lane;
}

scheduleUpdateOnFiber函數是React中Schedule模塊主要函數,用于調度更新,下面幾種情況都會調用scheduleUpdateOnFiber函數:

  • 頁面初次渲染
  • 類組件setState/forceUpdate
  • 函數組件setState

Schedule模塊流程圖

一、schdeuleUpdateOnFiber函數

function scheduleUpdateOnFiber(root, fiber, lane, eventTime) {checkForNestedUpdates();{if (isRunningInsertionEffect) {error('useInsertionEffect must not schedule updates.');}}{if (isFlushingPassiveEffects) {didScheduleUpdateDuringPassiveEffects = true;}} // Mark that the root has a pending update.markRootUpdated(root, lane, eventTime);if ((executionContext & RenderContext) !== NoLanes && root === workInProgressRoot) {// This update was dispatched during the render phase. This is a mistake// if the update originates from user space (with the exception of local// hook updates, which are handled differently and don't reach this// function), but there are some internal React features that use this as// an implementation detail, like selective hydration.warnAboutRenderPhaseUpdatesInDEV(fiber); // Track lanes that were updated during the render phase} else {// This is a normal update, scheduled from outside the render phase. For// example, during an input event.{if (isDevToolsPresent) {addFiberToLanesMap(root, fiber, lane);}}warnIfUpdatesNotWrappedWithActDEV(fiber);if (root === workInProgressRoot) {// Received an update to a tree that's in the middle of rendering. Mark// that there was an interleaved update work on this root. Unless the// `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render// phase update. In that case, we don't treat render phase updates as if// they were interleaved, for backwards compat reasons.if ( (executionContext & RenderContext) === NoContext) {workInProgressRootInterleavedUpdatedLanes = mergeLanes(workInProgressRootInterleavedUpdatedLanes, lane);}if (workInProgressRootExitStatus === RootSuspendedWithDelay) {// The root already suspended with a delay, which means this render// definitely won't finish. Since we have a new update, let's mark it as// suspended now, right before marking the incoming update. This has the// effect of interrupting the current render and switching to the update.// TODO: Make sure this doesn't override pings that happen while we've// already started rendering.markRootSuspended$1(root, workInProgressRootRenderLanes);}}ensureRootIsScheduled(root, eventTime);if (lane === SyncLane && executionContext === NoContext && (fiber.mode & ConcurrentMode) === NoMode && // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.!( ReactCurrentActQueue$1.isBatchingLegacy)) {// Flush the synchronous work now, unless we're already working or inside// a batch. This is intentionally inside scheduleUpdateOnFiber instead of// scheduleCallbackForFiber to preserve the ability to schedule a callback// without immediately flushing it. We only do this for user-initiated// updates, to preserve historical behavior of legacy mode.resetRenderTimer();flushSyncCallbacksOnlyInLegacyMode();}}
}

二、markRootUpdated函數? ? ??

負責標記根節點有新任務,并為調度系統提供最新的優先級和時間信息。

function markRootUpdated(root, updateLane, eventTime) {root.pendingLanes |= updateLane; // If there are any suspended transitions, it's possible this new update// could unblock them. Clear the suspended lanes so that we can try rendering// them again.//// TODO: We really only need to unsuspend only lanes that are in the// `subtreeLanes` of the updated fiber, or the update lanes of the return// path. This would exclude suspended updates in an unrelated sibling tree,// since there's no way for this update to unblock it.//// We don't do this if the incoming update is idle, because we never process// idle updates until after all the regular updates have finished; there's no// way it could unblock a transition.if (updateLane !== IdleLane) {root.suspendedLanes = NoLanes;root.pingedLanes = NoLanes;}var eventTimes = root.eventTimes;var index = laneToIndex(updateLane); // We can always overwrite an existing timestamp because we prefer the most// recent event, and we assume time is monotonically increasing.eventTimes[index] = eventTime;
}

三、ensureRootIsScheduled函數

確保當前 FiberRoot 上有一個合適優先級的調度任務被安排,即根據最新的更新優先級,決定是否需要新建、復用或取消調度任務,并最終調度同步或異步的渲染入口。

  1. 獲取下一個需要處理的 lanes(優先級),如果沒有任務,取消已存在的調度任務。
  2. 判斷當前任務優先級是否和已存在的調度任務一致,如果一致則復用,不一致則取消舊任務。
  3. 根據優先級安排調度:
    • 如果是同步優先級(SyncLane),調度?performSyncWorkOnRoot。
    • 否則,調度performConcurrentWorkOnRoot ,并傳遞合適的 scheduler 優先級。
  4. 將調度任務的引用和優先級記錄到 root 上。
function ensureRootIsScheduled(root, currentTime) {var existingCallbackNode = root.callbackNode; // Check if any lanes are being starved by other work. If so, mark them as// expired so we know to work on those next.markStarvedLanesAsExpired(root, currentTime); // Determine the next lanes to work on, and their priority.var nextLanes = getNextLanes(root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes);if (nextLanes === NoLanes) {// Special case: There's nothing to work on.if (existingCallbackNode !== null) {cancelCallback$1(existingCallbackNode);}root.callbackNode = null;root.callbackPriority = NoLane;return;} // We use the highest priority lane to represent the priority of the callback.var newCallbackPriority = getHighestPriorityLane(nextLanes); // Check if there's an existing task. We may be able to reuse it.var existingCallbackPriority = root.callbackPriority;if (existingCallbackPriority === newCallbackPriority && // Special case related to `act`. If the currently scheduled task is a// Scheduler task, rather than an `act` task, cancel it and re-scheduled// on the `act` queue.!( ReactCurrentActQueue$1.current !== null && existingCallbackNode !== fakeActCallbackNode)) {{// If we're going to re-use an existing task, it needs to exist.// Assume that discrete update microtasks are non-cancellable and null.// TODO: Temporary until we confirm this warning is not fired.if (existingCallbackNode == null && existingCallbackPriority !== SyncLane) {error('Expected scheduled callback to exist. This error is likely caused by a bug in React. Please file an issue.');}} // The priority hasn't changed. We can reuse the existing task. Exit.return;}if (existingCallbackNode != null) {// Cancel the existing callback. We'll schedule a new one below.cancelCallback$1(existingCallbackNode);} // Schedule a new callback.var newCallbackNode;if (newCallbackPriority === SyncLane) {// Special case: Sync React callbacks are scheduled on a special// internal queueif (root.tag === LegacyRoot) {if ( ReactCurrentActQueue$1.isBatchingLegacy !== null) {ReactCurrentActQueue$1.didScheduleLegacyUpdate = true;}scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root));} else {scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));}{// Flush the queue in a microtask.if ( ReactCurrentActQueue$1.current !== null) {// Inside `act`, use our internal `act` queue so that these get flushed// at the end of the current scope even when using the sync version// of `act`.ReactCurrentActQueue$1.current.push(flushSyncCallbacks);} else {scheduleMicrotask(function () {// In Safari, appending an iframe forces microtasks to run.// https://github.com/facebook/react/issues/22459// We don't support running callbacks in the middle of render// or commit so we need to check against that.if ((executionContext & (RenderContext | CommitContext)) === NoContext) {// Note that this would still prematurely flush the callbacks// if this happens outside render or commit phase (e.g. in an event).flushSyncCallbacks();}});}}newCallbackNode = null;} else {var schedulerPriorityLevel;switch (lanesToEventPriority(nextLanes)) {case DiscreteEventPriority:schedulerPriorityLevel = ImmediatePriority;break;case ContinuousEventPriority:schedulerPriorityLevel = UserBlockingPriority;break;case DefaultEventPriority:schedulerPriorityLevel = NormalPriority;break;case IdleEventPriority:schedulerPriorityLevel = IdlePriority;break;default:schedulerPriorityLevel = NormalPriority;break;}newCallbackNode = scheduleCallback$1(schedulerPriorityLevel, performConcurrentWorkOnRoot.bind(null, root));}root.callbackPriority = newCallbackPriority;root.callbackNode = newCallbackNode;
} // This is the entry point for every concurrent task, i.e. anything that
// goes through Scheduler.

四、performSyncWorkOnRoot函數

以同步方式執行根節點的渲染和提交流程,用于處理最高優先級(SyncLane)的更新。

function performSyncWorkOnRoot(root) {{syncNestedUpdateFlag();}if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {throw new Error('Should not already be working.');}flushPassiveEffects();var lanes = getNextLanes(root, NoLanes);if (!includesSomeLane(lanes, SyncLane)) {// There's no remaining sync work left.ensureRootIsScheduled(root, now());return null;}var exitStatus = renderRootSync(root, lanes);if (root.tag !== LegacyRoot && exitStatus === RootErrored) {// If something threw an error, try rendering one more time. We'll render// synchronously to block concurrent data mutations, and we'll includes// all pending updates are included. If it still fails after the second// attempt, we'll give up and commit the resulting tree.var errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);if (errorRetryLanes !== NoLanes) {lanes = errorRetryLanes;exitStatus = recoverFromConcurrentError(root, errorRetryLanes);}}if (exitStatus === RootFatalErrored) {var fatalError = workInProgressRootFatalError;prepareFreshStack(root, NoLanes);markRootSuspended$1(root, lanes);ensureRootIsScheduled(root, now());throw fatalError;}if (exitStatus === RootDidNotComplete) {throw new Error('Root did not complete. This is a bug in React.');} // We now have a consistent tree. Because this is a sync render, we// will commit it even if something suspended.var finishedWork = root.current.alternate;root.finishedWork = finishedWork;root.finishedLanes = lanes;commitRoot(root, workInProgressRootRecoverableErrors, workInProgressTransitions); // Before exiting, make sure there's a callback scheduled for the next// pending level.ensureRootIsScheduled(root, now());return null;
}function flushRoot(root, lanes) {if (lanes !== NoLanes) {markRootEntangled(root, mergeLanes(lanes, SyncLane));ensureRootIsScheduled(root, now());if ((executionContext & (RenderContext | CommitContext)) === NoContext) {resetRenderTimer();flushSyncCallbacks();}}
}

五、performConcurrentWorkOnRoot函數

并發渲染的核心入口,負責調度和執行并發模式下的 Fiber 樹渲染。

function performConcurrentWorkOnRoot(root, didTimeout) {{resetNestedUpdateFlag();} // Since we know we're in a React event, we can clear the current// event time. The next update will compute a new event time.currentEventTime = NoTimestamp;currentEventTransitionLane = NoLanes;if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {throw new Error('Should not already be working.');} // Flush any pending passive effects before deciding which lanes to work on,// in case they schedule additional work.var originalCallbackNode = root.callbackNode;var didFlushPassiveEffects = flushPassiveEffects();if (didFlushPassiveEffects) {// Something in the passive effect phase may have canceled the current task.// Check if the task node for this root was changed.if (root.callbackNode !== originalCallbackNode) {// The current task was canceled. Exit. We don't need to call// `ensureRootIsScheduled` because the check above implies either that// there's a new task, or that there's no remaining work on this root.return null;}} // Determine the next lanes to work on, using the fields stored// on the root.var lanes = getNextLanes(root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes);if (lanes === NoLanes) {// Defensive coding. This is never expected to happen.return null;} // We disable time-slicing in some cases: if the work has been CPU-bound// for too long ("expired" work, to prevent starvation), or we're in// sync-updates-by-default mode.// TODO: We only check `didTimeout` defensively, to account for a Scheduler// bug we're still investigating. Once the bug in Scheduler is fixed,// we can remove this, since we track expiration ourselves.var shouldTimeSlice = !includesBlockingLane(root, lanes) && !includesExpiredLane(root, lanes) && ( !didTimeout);var exitStatus = shouldTimeSlice ? renderRootConcurrent(root, lanes) : renderRootSync(root, lanes);if (exitStatus !== RootInProgress) {if (exitStatus === RootErrored) {// If something threw an error, try rendering one more time. We'll// render synchronously to block concurrent data mutations, and we'll// includes all pending updates are included. If it still fails after// the second attempt, we'll give up and commit the resulting tree.var errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);if (errorRetryLanes !== NoLanes) {lanes = errorRetryLanes;exitStatus = recoverFromConcurrentError(root, errorRetryLanes);}}if (exitStatus === RootFatalErrored) {var fatalError = workInProgressRootFatalError;prepareFreshStack(root, NoLanes);markRootSuspended$1(root, lanes);ensureRootIsScheduled(root, now());throw fatalError;}if (exitStatus === RootDidNotComplete) {// The render unwound without completing the tree. This happens in special// cases where need to exit the current render without producing a// consistent tree or committing.//// This should only happen during a concurrent render, not a discrete or// synchronous update. We should have already checked for this when we// unwound the stack.markRootSuspended$1(root, lanes);} else {// The render completed.// Check if this render may have yielded to a concurrent event, and if so,// confirm that any newly rendered stores are consistent.// TODO: It's possible that even a concurrent render may never have yielded// to the main thread, if it was fast enough, or if it expired. We could// skip the consistency check in that case, too.var renderWasConcurrent = !includesBlockingLane(root, lanes);var finishedWork = root.current.alternate;if (renderWasConcurrent && !isRenderConsistentWithExternalStores(finishedWork)) {// A store was mutated in an interleaved event. Render again,// synchronously, to block further mutations.exitStatus = renderRootSync(root, lanes); // We need to check again if something threwif (exitStatus === RootErrored) {var _errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);if (_errorRetryLanes !== NoLanes) {lanes = _errorRetryLanes;exitStatus = recoverFromConcurrentError(root, _errorRetryLanes); // We assume the tree is now consistent because we didn't yield to any// concurrent events.}}if (exitStatus === RootFatalErrored) {var _fatalError = workInProgressRootFatalError;prepareFreshStack(root, NoLanes);markRootSuspended$1(root, lanes);ensureRootIsScheduled(root, now());throw _fatalError;}} // We now have a consistent tree. The next step is either to commit it,// or, if something suspended, wait to commit it after a timeout.root.finishedWork = finishedWork;root.finishedLanes = lanes;finishConcurrentRender(root, exitStatus, lanes);}}ensureRootIsScheduled(root, now());if (root.callbackNode === originalCallbackNode) {// The task node scheduled for this root is the same one that's// currently executed. Need to return a continuation.return performConcurrentWorkOnRoot.bind(null, root);}return null;
}

六、prepareFreshStack函數

prepareFreshStack的作用是為一次新的 Fiber 樹渲染初始化全局狀態,主要包括:

  • 重置當前 root 的渲染相關狀態(如 finishedWork、finishedLanes 等)。
  • 取消上一次渲染遺留的超時(timeout)。
  • 如果上一次渲染被中斷,清理未完成的 workInProgress。
  • 創建新的 workInProgress Fiber 樹(即 root.current 的 alternate),并將全局變量 workInProgress指向它。
  • 設置本次渲染的 lanes(優先級)。
  • 重置本次渲染相關的全局變量(如 workInProgressRoot、workInProgressRootRenderLanes 等)。
  • 清空上次渲染遺留的錯誤、跳過的 lanes、pinged lanes 等。
function prepareFreshStack(root, lanes) {root.finishedWork = null;root.finishedLanes = NoLanes;var timeoutHandle = root.timeoutHandle;if (timeoutHandle !== noTimeout) {// The root previous suspended and scheduled a timeout to commit a fallback// state. Now that we have additional work, cancel the timeout.root.timeoutHandle = noTimeout; // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check abovecancelTimeout(timeoutHandle);}if (workInProgress !== null) {var interruptedWork = workInProgress.return;while (interruptedWork !== null) {var current = interruptedWork.alternate;unwindInterruptedWork(current, interruptedWork);interruptedWork = interruptedWork.return;}}workInProgressRoot = root;var rootWorkInProgress = createWorkInProgress(root.current, null);workInProgress = rootWorkInProgress;workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;workInProgressRootExitStatus = RootInProgress;workInProgressRootFatalError = null;workInProgressRootSkippedLanes = NoLanes;workInProgressRootInterleavedUpdatedLanes = NoLanes;workInProgressRootPingedLanes = NoLanes;workInProgressRootConcurrentErrors = null;workInProgressRootRecoverableErrors = null;finishQueueingConcurrentUpdates();{ReactStrictModeWarnings.discardPendingWarnings();}return rootWorkInProgress;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/89271.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/89271.shtml
英文地址,請注明出處:http://en.pswp.cn/web/89271.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Unity3D + VS2022連接雷電模擬器調試

本文參考了Unity3D Profiler 連接真機和模擬器_unity 連接雷電模擬器-CSDN博客 具體步驟: 1、cmd打開命令窗口,輸入adb devices,確認能檢測到模擬器 示例:List of devices attached emulator-5554 device 2、…

學習軟件測試的第十五天

1.會寫測試用例嗎?測試用例有什么要素“會的,我寫過多個功能測試和接口測試的測試用例。我寫用例的時候會根據需求文檔或原型圖分析測試點,然后從正常流程、異常流程、邊界情況等方面設計測試場景。每條用例我都會包含:用例編號、…

C++硬實時調度:原理、實踐與最佳方案

在工業自動化、航空航天、醫療設備等領域,系統的實時性往往直接關系到生命安全和財產損失。C作為高性能編程語言,為硬實時系統開發提供了強大支持。本文將深入探討C硬實時調度的核心技術,從操作系統原理到代碼實現的全方位解析。 一、實時系統…

LeetCode 1156.單字符重復子串的最大長度

如果字符串中的所有字符都相同,那么這個字符串是單字符重復的字符串。 給你一個字符串 text,你只能交換其中兩個字符一次或者什么都不做,然后得到一些單字符重復的子串。返回其中最長的子串的長度。 示例 1: 輸入:text…

K近鄰算法的分類與回歸應用場景

K近鄰算法的分類與回歸應用場景 K近鄰(K-Nearest Neighbors, KNN)算法是一種基礎但強大的機器學習方法,它既可以用于分類問題,也能解決回歸問題。 兩者的核心思想都是基于"近朱者赤,近墨者黑"的原理&#xf…

算法精講--正則表達式(二):分組、引用與高級匹配技術

算法精講–正則表達式(二):分組、引用與高級匹配技術 🚀正則表達式的真正力量在于組合使用各種語法元素,創造出強大而精確的匹配模式! —— 作者:無限大 推薦閱讀時間:25 分鐘 適用人…

python+requests 接口自動化測試實戰

首先介紹一下python的requests模塊: requests的使用介紹:requests快速入門 Python結合requests庫實現接口自動化測試環境說明: 1.WIN 7, 64位 2.Python3.4.3 (pip-8.1.2) 3.Requests —>pip install requests 4.U…

NAT 實驗

NAT 實驗 一.實驗拓撲圖實驗目的 1.按照圖示配置 IP 地址 2.私網 A 通過 R1 接入到互聯網,私網 B 通過 R3 接入到互聯網 3.私網 A 內部存在 Vlan10 和 Vlan20,通過 R1 上單臂路由訪問外部網絡 4.私網 A 通過 NAPT 使 Vlan10 和 Vlan20 都能夠使用 R1 的公…

buuctf——web刷題第三頁

第三頁 目錄 [FBCTF2019]RCEService [0CTF 2016]piapiapia [Zer0pts2020]Can you guess it? [WUSTCTF2020]顏值成績查詢 [SUCTF 2019]Pythonginx [MRCTF2020]套娃 [CSCCTF 2019 Qual]FlaskLight [watevrCTF-2019]Cookie Store [WUSTCTF2020]CV Maker [紅明谷CTF 202…

前后端分離項目中的接口設計與調用流程——以高仙機器人集成為例

一、背景介紹在前后端分離項目開發中,前端頁面需要頻繁調用后端接口獲取數據。在高仙機器人對接項目中,我們采用了若依(RuoYi)框架,前端通過統一的 API 封裝與后端進行數據交互,而后端再對接高仙官方的 OPE…

【第五節】部署http接口到ubuntu server上的docker內

描述清楚需求,讓deepseek幫我們寫一個demo,文件結構如下 FLASK_API_001 ├── app.py └── Dockerfile └── requirements.txtapp.pyfrom flask import Flask, jsonify, requestapp Flask(__name__)# 根路由 app.route(/) def home():return "…

在 IntelliJ IDEA 中添加框架支持的解決方案(沒有出現Add Framework Support)

在 IntelliJ IDEA 中添加框架支持的解決方案 問題背景 版本變化:在 IntelliJ IDEA 2023.2 及更高版本中,項目右鍵菜單中的 “Add Framework Support” 選項已被移除。 常見需求:為 Java 項目添加框架支持(如 Maven、Spring 等&am…

北京-4年功能測試2年空窗-報培訓班學測開-第五十天

咦,昨天路上寫一半就到家了,后來想早點睡就忘了還要發了,現在趕緊補上昨天是最后一節課(我們將一整天的課稱為一節),這就結課了昨天講了簡歷編寫,面試要準備的內容,還有redis和docker也沒有什么…

華為鴻蒙HarmonyOpenEye項目:開眼App的鴻蒙實現之旅

華為鴻蒙HarmonyOpenEye項目:開眼App的鴻蒙實現之旅 引言 在當今移動應用開發的浪潮中,鴻蒙系統憑借其獨特的分布式能力和高效的開發框架,吸引了眾多開發者的目光。今天要給大家介紹的是一個基于華為鴻蒙系統開發的開眼App項目——HarmonyO…

代碼隨想錄day36dp4

文章目錄1049.最后一塊石頭的重量II494.目標和474.一和零1049.最后一塊石頭的重量II 題目鏈接 文章講解 class Solution { public:int lastStoneWeightII(vector<int>& stones) {// 1. 確定 DP 數組及下標的含義&#xff1a;// dp[i][j] 表示考慮前 i 塊石頭&#…

Python 爬蟲實戰指南:按關鍵字搜索商品

在電商領域&#xff0c;按關鍵字搜索商品并獲取其詳情信息是一項常見的需求。無論是進行市場調研、競品分析還是用戶體驗優化&#xff0c;能夠快速準確地獲取商品信息都至關重要。1688 作為國內領先的 B2B 電商平臺&#xff0c;提供了豐富的商品資源。本文將詳細介紹如何使用 P…

【源力覺醒 創作者計劃】百度AI的開放新篇章:文心4.5本地化部署指南與未來生態戰略展望

百度AI的開放新篇章&#xff1a;文心4.5本地化部署指南與未來生態戰略展望 一起來玩轉文心大模型吧&#x1f449;文心大模型免費下載地址&#xff1a;https://ai.gitcode.com/theme/1939325484087291906 &#x1f31f; 嗨&#xff0c;我是IRpickstars&#xff01; &#x1f30…

測試工作中的質量門禁管理

一、前言 測試階段的質量門禁設計要考慮幾個維度,首先是研發流程的階段劃分,每個階段都要有明確的準入準出標準;其次要考慮不同測試類型的特點,比如功能測試和性能測試的驗收標準肯定不同;最后還要平衡質量要求和項目進度。 在單元測試階段,可以設置通過率和覆蓋率的閾值…

線上分享:解碼eVTOL安全基因,構建安全飛行生態

隨著城市空中交通&#xff08;UAM&#xff09;快速發展&#xff0c;電動垂直起降飛行器&#xff08;eVTOL&#xff09;面臨嚴格的安全與可靠性要求&#xff0c;需滿足全球適航標準及全生命周期分析。安全與可靠的飛行系統成為行業關注的焦點。在此背景下&#xff0c;本期線上分…

C回調函數基礎用法

&#x1f4cc; 定義&#xff1a;回調函數是通過函數指針傳遞給另一個函數的函數&#xff0c;這個被傳進去的函數將在某個時刻被“回調”調用。換句話說&#xff1a;你定義一個函數 A把函數 A 的地址&#xff08;即函數指針&#xff09;作為參數傳給函數 B函數 B 在合適的時機調…