在这一章节的内容中,我们主要对 Flux 的 Dispatcher.js 源码进行分析,因为在我们之前的案例中只引用这个文件。关于 Flux 中其他的工具文件的源码分析,等后面有时间了,再出相关的文章!

Flux 源码地址:https://github.com/facebook/flux

准备工作

Flux 的源码结构比较简单,大家就自行根据上面的 github 地址进行 clone 就好了。

Flux 所有的源码存放在 src 目录,src 目录下的结构也很常规,包括 container、stores、utils 等目录和其他源码文件。这里我们主要分析的是 Dispatcher.js 文件。

源码分析

下面的内容,我们将对 Dispatcher.js 文件源代码逐部分的进行分析!

开头

  1. 'use strict';
  2. var invariant = require('invariant');
  3. export type DispatchToken = string;
  4. var _prefix = 'ID_';

首先使用了代码的严格模式,这是框架和类库的一贯风格。然后引入了 Facebook 自己的错误提示库,接着导出了一个 token,其实一个 Flux 的回调代码块有唯一的一个 token,这个 token 就是用来让别人 Invokoe 你代码的。最后定义了一个前缀变量,供后面的逻辑使用。

整体

Dispatcher.js 核心逻辑代码被包裹在一个 class 语法里面,最后用 commonjs 模块化规范进行导出。

  1. class Dispatcher<TPayload> {
  2. _callbacks: {[key: DispatchToken]: (payload: TPayload) => void};
  3. _isDispatching: boolean;
  4. _isHandled: {[key: DispatchToken]: boolean};
  5. _isPending: {[key: DispatchToken]: boolean};
  6. _lastID: number;
  7. _pendingPayload: TPayload;
  8. constructor() {
  9. this._callbacks = {};
  10. this._isDispatching = false;
  11. this._isHandled = {};
  12. this._isPending = {};
  13. this._lastID = 1;
  14. }
  15. }
  16. module.exports = Dispatcher;

这里定义了 Dispatcher 类,类里面有给静态变量赋值的构造函数。是不是看到有很多 DispatchToken 的东西?这个就是我们之前说的 Token 了,每一个回调函数的唯一标识。

  • _callbacks:就是 DispatchToken 和函数回调的一个 Dictionary。

  • _isDispatching:体现当前 Dispatcher 是否处于 dispatch 状态。

  • _isHandled:通过 token 去检测一个函数是否被处理过了。

  • _isPending:通过 token 去检测一个函数是否被提交 Dispatcher 了。

  • _lastID:最近一次被加入 Dispatcher 的函数体的唯一标识,即 DispatchToken。

  • _pendingPayload:需要传递给调用函数的参数。

register 方法

  1. register(callback: (payload: TPayload) => void): DispatchToken {
  2. var id = _prefix + this._lastID++;
  3. this._callbacks[id] = callback;
  4. return id;
  5. }

register 逻辑极其简单,首先根据之前的一些变量获取注册的回调函数的唯一标识(DispatchToken) _prefix + this._lastID++ 对于 JavaScript 这样的单线程语言,这无疑是最高效安全的方法了。然后以这个唯一标识为 key ,回调函数为 value 保存到了 _callbacks 静态对象里面。最后返回了这个唯一标识。

unregister

有注册方法就有取消注册的方法!

  1. unregister(id: DispatchToken): void {
  2. invariant(
  3. this._callbacks[id],
  4. 'Dispatcher.unregister(...): `%s` does not map to a registered callback.',
  5. id
  6. );
  7. delete this._callbacks[id];
  8. }

调用取消注册方法时需要传入需要取消注册回调函数的 DispatchToken,并对逻辑处理做了异常处理,最后使用 delete 直接删除了以 DispatchToken 为 key 的属性值。

dispatch

  1. dispatch(payload: TPayload): void {
  2. invariant(
  3. !this._isDispatching,
  4. 'Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch.'
  5. );
  6. this._startDispatching(payload);
  7. try {
  8. for (var id in this._callbacks) {
  9. if (this._isPending[id]) {
  10. continue;
  11. }
  12. this._invokeCallback(id);
  13. }
  14. } finally {
  15. this._stopDispatching();
  16. }
  17. }

整个函数的逻辑就是:首先是判断当前 Dispatcher 是否已经处于 Dispatching 状态中了,如果是,不能打断。然后通过 _startDispatching 更新状态。更新状态结束以后,将非 pending 状态的 callback 通过 _invokeCallback 执行( pending 在这里的含义可以简单理解为:正在执行或者锁死)。所有执行完了以后,通过 _stopDispatching 恢复状态。

isDispatching

返回当前 Dispatcher 是否在 dispatching 状态。

  1. isDispatching(): boolean {
  2. return this._isDispatching;
  3. }

startDispatching

首先该函数将所有注册的 callback 的状态都清空,将传入的 action 赋给静态变量,表示正在执行的 action, 并标记 Dispatcher 的状态进入 dispatching。

  1. _startDispatching(payload: TPayload): void {
  2. for (var id in this._callbacks) {
  3. this._isPending[id] = false;
  4. this._isHandled[id] = false;
  5. }
  6. this._pendingPayload = payload;
  7. this._isDispatching = true;
  8. }

stopDispatching

删除静态变量存储的 action 的值,然后将 Dispatcher 的状态退出 dispatching。

  1. _stopDispatching(): void {
  2. delete this._pendingPayload;
  3. this._isDispatching = false;
  4. }

invokeCallback

  1. _invokeCallback(id: DispatchToken): void {
  2. this._isPending[id] = true;
  3. this._callbacks[id](this._pendingPayload);
  4. this._isHandled[id] = true;
  5. }

当真正调用 callback 之前将其状态设置为 pending ,执行完成之后设置为handled。

waitFor

  1. waitFor(ids: Array<DispatchToken>): void {
  2. invariant(
  3. this._isDispatching,
  4. 'Dispatcher.waitFor(...): Must be invoked while dispatching.'
  5. );
  6. for (var ii = 0; ii < ids.length; ii++) {
  7. var id = ids[ii];
  8. if (this._isPending[id]) {
  9. invariant(
  10. this._isHandled[id],
  11. 'Dispatcher.waitFor(...): Circular dependency detected while ' +
  12. 'waiting for `%s`.',
  13. id
  14. );
  15. continue;
  16. }
  17. invariant(
  18. this._callbacks[id],
  19. 'Dispatcher.waitFor(...): `%s` does not map to a registered callback.',
  20. id
  21. );
  22. this._invokeCallback(id);
  23. }
  24. }
  • 首先是用一个 Invariant 函数判断当前必须处于 Dispatching 状态。如果不处于Dispatching状态中,那么说明当前没有函数正在执行。

  • 然后对 DispatchToken 数组进行遍历,如果遍历到的 DispatchToken 处于 pending 状态,就跳过,继续执行下一个遍历。

  • 但是在这里有个「循环依赖」需要进行检查。试想如下情况:如果 A函数依赖 B函数的 Token, 同时 B函数依赖 A函数的 Token,这样就会造成「“死锁”」。所以,当一个函数依赖的对象处于 pending 状态时,说明这个函数已经开始执行了,但是如果同时该函数没有进入 handled 状态,说明该函数也被锁死了。

  • 检查 token 对应的 callback 是否存在。

  • 调用这个 token 对应的函数。

可以看出,waitFor 就是一个等待函数,当 B函数执行时,执行到某些条件不满足的时候(我们称之为依赖没解决),就是等待依赖去完成。有点类似下面的函数:

  1. while (!satisfied) ;
  2. doSomething

总结

这一溜下来,思路还是非常清晰的,没有什么复杂的地方。Dispatcher.js 源代码文件注释也很清晰,基本上是每个函数都有注释。

看完代码还是很有启发性的。之前一直觉得看源码是一件很痛苦的事情,知道看了 Dispatcher.js 源代码。学习继续,好好加油!