尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Flux Dispatcher 完全指南:从 API 到 waitFor 依赖编排原理

发布时间:2026/9/21 2:05:50

资讯中心
01
ARTICLE

Flux Dispatcher 完全指南:从 API 到 waitFor 依赖编排原理

Flux Dispatcher 完全指南:从 API 到 waitFor 依赖编排原理
Flux Dispatcher 完全指南从 API 到 waitFor 依赖编排原理【免费下载链接】fluxApplication Architecture for Building User Interfaces项目地址: https://gitcode.com/gh_mirrors/fl/fluxFlux 是一套用于构建用户界面的单向数据流应用架构而Dispatcher是整个架构中唯一不依赖 React、也不依赖任何第三方库的枢纽组件——所有 Action 都经由它广播给各个 Store 的回调函数。本指南以仓库中的 docs/Dispatcher.md 为骨架结合 Dispatcher.js 源码与 Dispatcher-test.js 测试用例深入讲解 Dispatcher 的 API、waitFor()依赖编排机制以及它与 Store 协作的底层原理读完即可在自己的 Flux 应用中正确使用 Dispatcher 并规避循环依赖等经典陷阱。Dispatcher 是什么与通用发布/订阅系统的本质区别Dispatcher用于把 payload载荷广播给所有已注册的回调函数。它和通用的 pub-sub发布/订阅系统有两点本质区别回调不订阅特定事件每个 payload 都会被派发给每一个已注册的回调。也就是说回调无法只关心某类事件而是必须自己判断 payload 是否与自己相关。回调可以被整体或部分地延迟执行通过waitFor()一个回调可以等待其他回调执行完毕后再继续从而在多个 Store 之间建立确定的依赖顺序。从源码结构看Dispatcher.js 是一个泛型类DispatcherTPayload内部维护了_callbacks、_isDispatching、_isHandled、_isPending四张状态表这正是它区别于普通事件发射器的核心数据结构。为什么需要这种设计在 Flux 中一个用户操作往往需要多个 Store 同时响应而这些 Store 之间存在数据依赖。例如选择国家后自动选择默认城市、再计算航班价格如果不保证执行顺序价格计算就可能读到过期的城市数据。Dispatcher 的waitFor()正是为了解决这种依赖更新的问题而生的。API 全景Dispatcher 暴露了 5 个核心方法文档中的完整签名如下方法签名说明registerregister(function callback): string注册一个回调每次 dispatch 时都会被调用。返回一个 token令牌可用于waitFor()unregisterunregister(string id): void根据 token 移除一个已注册的回调waitForwaitFor(arraystring ids): void等待指定 token 对应的回调执行完毕再继续执行当前回调。只能在回调响应某次 dispatch 的过程中调用dispatchdispatch(object payload): void向所有已注册回调派发一个 payloadisDispatchingisDispatching(): boolean查询 Dispatcher 当前是否正处于派发过程中registertoken 的生成规则从 Dispatcher.js 可以看到 token 的生成实现var _prefix ID_; register(callback) { var id _prefix this._lastID; // 例如 ID_1、ID_2、ID_3 ... this._callbacks[id] callback; return id; }_lastID从 1 开始自增所以第一次注册返回的 token 是ID_1第二次是ID_2依此类推。token 的类型在源码中被定义为DispatchToken string见 Dispatcher.js它只是一个字符串标识不是Promise 或句柄。unregister按 token 移除回调Dispatcher.js 中的实现使用了invariant做防御性校验如果传入的 token 没有对应任何已注册回调会直接抛出错误Dispatcher.unregister(...):does not map to a registered callback.。移除后该回调在后续 dispatch 中不会再被调用测试用例should properly unregister callbacks验证了这一点见 Dispatcher-test.js。实战示例航班目的地表单文档用航班目的地表单这一假想场景完整演示了 Dispatcher 的核心用法。假设表单在选中国家后自动选择该国家的默认城市并根据国家 城市计算基础票价。第一步创建 Dispatcher 与 Storevar flightDispatcher new Dispatcher(); // 记录当前选中的国家 var CountryStore {country: null}; // 记录当前选中的城市 var CityStore {city: null}; // 记录当前选中城市的基础票价 var FlightPriceStore {price: null};注意这里的CountryStore等只是普通 JavaScript 对象用于演示 Dispatcher 的机制。在真实 Flux 应用中Store 通常基于EventEmitter或仓库提供的FluxStore基类构建并在构造时通过dispatcher.register(...)注册回调详见下文与 FluxStore 的协作。第二步派发城市更新 payload用户更改了选中的城市后通过dispatch广播flightDispatcher.dispatch({ actionType: city-update, selectedCity: paris, });该 payload 被CityStore消化flightDispatcher.register(function (payload) { if (payload.actionType city-update) { CityStore.city payload.selectedCity; } });注意这里体现了 Dispatcher 的第一个特性这个回调虽然只关心city-update但它每次 dispatch 都会被调用只是通过if判断主动忽略了不相关的 payload。第三步派发国家更新 payloadflightDispatcher.dispatch({ actionType: country-update, selectedCountry: australia, });该 payload 同时被两个 Store 消化CountryStore.dispatchToken flightDispatcher.register(function (payload) { if (payload.actionType country-update) { CountryStore.country payload.selectedCountry; } });注册CountryStore回调时把返回的 token 保存在CountryStore.dispatchToken上。有了这个 token后续回调就可以用waitFor()声明自己对它的依赖。第四步用 waitFor 保证依赖顺序当CityStore的回调需要根据新国家计算默认城市时它必须先确认CountryStore已经更新完毕CityStore.dispatchToken flightDispatcher.register(function (payload) { if (payload.actionType country-update) { // 注意此时 CountryStore.country 可能尚未更新 flightDispatcher.waitFor([CountryStore.dispatchToken]); // 执行到这里时CountryStore.country 已保证被更新 // 为新国家选择默认城市 CityStore.city getDefaultCityForCountry(CountryStore.country); } });waitFor([CountryStore.dispatchToken])的含义是暂停当前回调的执行先确保CountryStore的回调已经执行完毕再继续。这就是 Dispatcher 的第二个特性——回调可以被整体或部分地延迟。waitFor 可以链式调用依赖是可以叠加的。FlightPriceStore的价格计算同时依赖国家和城市可以这样写FlightPriceStore.dispatchToken flightDispatcher.register(function (payload) { switch (payload.actionType) { case country-update: case city-update: flightDispatcher.waitFor([CityStore.dispatchToken]); FlightPriceStore.price getFlightPriceStore( CountryStore.country, CityStore.city, ); break; } });最终country-updatepayload 会保证按CountryStore→CityStore→FlightPriceStore的顺序依次调用各 Store 注册的回调。waitFor 的底层原理pending / handled 状态机waitFor之所以能保证顺序靠的是 Dispatcher 内部维护的_isPending和_isHandled两张状态表以及_invokeCallback的记账逻辑。整个 dispatch 周期分为三个阶段见 Dispatcher.js_startDispatching(payload)把每个回调的_isPending和_isHandled重置为false保存_pendingPayload并将_isDispatching置为true。遍历_callbacks执行对每个未被标记为 pending 的回调调用_invokeCallback(id)。_invokeCallback会先把该回调标记为_isPending[id] true再执行回调最后标记_isHandled[id] true见 Dispatcher.js。_stopDispatching()删除_pendingPayload把_isDispatching恢复为false。这一步骤放在finally块中即使回调抛异常也会执行确保 Dispatcher 不会卡死在派发中状态。waitFor的执行逻辑见 Dispatcher.js正是基于这两张表waitFor(ids) { for (var ii 0; ii ids.length; ii) { var id ids[ii]; if (this._isPending[id]) { // 目标回调已经或正在执行若它还没执行完说明形成循环依赖直接抛错 invariant( this._isHandled[id], Dispatcher.waitFor(...): Circular dependency detected while waiting for %s., id, ); continue; // 已执行完毕无需再等待 } invariant( this._callbacks[id], Dispatcher.waitFor(...): %s does not map to a registered callback., id, ); this._invokeCallback(id); // 立即同步执行目标回调 } }这段代码揭示了几个关键事实waitFor是同步递归执行目标回调而不是异步等待。因此调用链上不存在微任务/宏任务调度顺序是确定性的。如果一个目标回调已经被执行完毕_isPending为 true 且_isHandled为 truewaitFor直接continue跳过不会重复执行——这正是多个回调对同一 store 调用waitFor不会重复更新的原因。如果一个目标回调正在执行中_isPending为 true 但_isHandled为 false说明出现了循环等待直接抛出Circular dependency detected错误。三个必须遵守的约束附源码级验证Dispatcher 对使用方式有硬性约束违反即抛错。这些行为都被测试用例逐一验证过1. 禁止在 dispatch 过程中再次 dispatch。dispatcher.register((payload) { dispatcher.dispatch(payload); // 抛错 });Dispatcher.js 中dispatch首先检查_isDispatching为 true 时抛出Cannot dispatch in the middle of a dispatch.。对应测试should throw if dispatch() while dispatchingDispatcher-test.js。2. 禁止在非派发状态下调用waitFor。dispatcher.waitFor([tokenA]); // 抛错Must be invoked while dispatching.waitFor的第一行 invariant 就是检查_isDispatching见 Dispatcher.js。对应测试should throw if waitFor() while not dispatchingDispatcher-test.js。3. 禁止形成循环依赖。包括自我等待和互相等待两种形态// 自我循环A 等待 A const tokenA dispatcher.register((payload) { dispatcher.waitFor([tokenA]); // 抛错Circular dependency detected }); // 相互循环A 等待 BB 又等待 A const tokenA dispatcher.register((payload) { dispatcher.waitFor([tokenB]); }); const tokenB dispatcher.register((payload) { dispatcher.waitFor([tokenA]); });对应测试should throw on self-circular dependencies与should throw on multi-circular dependenciesDispatcher-test.js。循环依赖检测正是通过上述 pending/handled 状态机在运行时完成的。此外还有两个值得注意的边界行为失败的 dispatch 不会破坏状态一致性即使某个回调抛异常finally块中的_stopDispatching()也会执行后续 dispatch 依然可用。测试should remain in a consistent state after a failed dispatchDispatcher-test.js验证了这一点。waitFor传入不存在的 token 会抛错对应测试should throw if waitFor() with invalid tokenDispatcher-test.js。与 FluxStore / ReduceStore 的协作dispatchToken 从哪来文档示例中dispatchToken是手动保存的而真实 Flux 应用中token 通常由 Store 基类自动管理。看 FluxStore.js 的构造函数constructor(dispatcher) { ... this.__dispatcher dispatcher; this._dispatchToken dispatcher.register((payload) { this.__invokeOnDispatch(payload); }); } getDispatchToken() { return this._dispatchToken; // 供其他 Store 的 waitFor 使用 }也就是说每个基于FluxStore创建的 Store 在实例化时就会把自己的分发回调注册进 Dispatcher并通过getDispatchToken()暴露 token。于是 Store 之间的依赖可以写成class CityStore extends FluxStore { __onDispatch(payload) { if (payload.actionType country-update) { this.getDispatcher().waitFor([CountryStore.getDispatchToken()]); this._city getDefaultCityForCountry(CountryStore.getCountry()); } } }从源码结构看FluxStore的__invokeOnDispatchFluxStore.js会在每轮 dispatch 开始时重置__changed并在子类处理完 payload 后通过 EventEmitter 广播change事件——这就是Store 变更通知视图的底层机制而这一切的入口都始于 Dispatcher 的那次register。在项目中使用 Dispatcher通过 npm 安装Flux 以 npm 模块发布在package.json中添加依赖或直接运行npm install flux即可。安装后通过命名空间访问const Dispatcher require(flux).Dispatcher;从仓库构建克隆本仓库并进入flux目录后运行npm installGulp 构建任务会自动生成Flux.js文件之后可以这样引入const Dispatcher require(path/to/this/directory/Flux).Dispatcher;构建过程还会在lib目录下生成去掉语法糖的Dispatcher与invariant模块可以直接拷贝到任何目录单独使用——仓库中的flux-todomvc等示例应用就是这么做的。推荐的单例模式在实际应用中整个应用通常共享一个Dispatcher 实例。仓库示例的写法可供参考// examples/flux-todomvc/src/data/TodoDispatcher.js import {Dispatcher} from flux; export default new Dispatcher();// examples/flux-flow/src/AppDispatcher.js带 Flow 泛型约束 import type {Action} from ./AppActions; import {Dispatcher} from flux; const dispatcher: DispatcherAction new Dispatcher(); export default dispatcher;之所以强调单例是因为如果多个模块各自new Dispatcher()Store 之间就无法通过waitFor建立依赖token 属于不同实例互不可见整个数据流的确定性顺序也就无从谈起。小结Flux 的 Dispatcher 用不到两百行代码实现了三个关键能力全量广播每个 payload 到达每个回调、依赖编排waitFor保证执行顺序、运行时防护禁止嵌套 dispatch、禁止非派发期调用 waitFor、检测循环依赖。理解它的核心在于记住_isPending/_isHandled状态机waitFor是同步递归、天然去重、且能识别循环的。若想深入验证这些行为可以直接阅读 Dispatcher.js 源码并运行仓库中的 Dispatcher-test.js 测试套件——十个测试用例覆盖了从基本广播到失败恢复的全部边界场景。【免费下载链接】fluxApplication Architecture for Building User Interfaces项目地址: https://gitcode.com/gh_mirrors/fl/flux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。