在这一章节的内容中,我们主要对 Flux 的 Dispatcher.js 源码进行分析,因为在我们之前的案例中只引用这个文件。关于 Flux 中其他的工具文件的源码分析,等后面有时间了,再出相关的文章!
Flux 源码地址:https://github.com/facebook/flux
准备工作
Flux 的源码结构比较简单,大家就自行根据上面的 github 地址进行 clone 就好了。
Flux 所有的源码存放在 src
目录,src 目录下的结构也很常规,包括 container、stores、utils 等目录和其他源码文件。这里我们主要分析的是 Dispatcher.js 文件。
源码分析
下面的内容,我们将对 Dispatcher.js 文件源代码逐部分的进行分析!
开头
'use strict';
var invariant = require('invariant');
export type DispatchToken = string;
var _prefix = 'ID_';
首先使用了代码的严格模式,这是框架和类库的一贯风格。然后引入了 Facebook 自己的错误提示库,接着导出了一个 token,其实一个 Flux 的回调代码块有唯一的一个 token,这个 token 就是用来让别人 Invokoe 你代码的。最后定义了一个前缀变量,供后面的逻辑使用。
整体
Dispatcher.js 核心逻辑代码被包裹在一个 class 语法里面,最后用 commonjs 模块化规范进行导出。
class Dispatcher<TPayload> {
_callbacks: {[key: DispatchToken]: (payload: TPayload) => void};
_isDispatching: boolean;
_isHandled: {[key: DispatchToken]: boolean};
_isPending: {[key: DispatchToken]: boolean};
_lastID: number;
_pendingPayload: TPayload;
constructor() {
this._callbacks = {};
this._isDispatching = false;
this._isHandled = {};
this._isPending = {};
this._lastID = 1;
}
}
module.exports = Dispatcher;
这里定义了 Dispatcher 类,类里面有给静态变量赋值的构造函数。是不是看到有很多 DispatchToken 的东西?这个就是我们之前说的 Token 了,每一个回调函数的唯一标识。
_callbacks:就是 DispatchToken 和函数回调的一个 Dictionary。
_isDispatching:体现当前 Dispatcher 是否处于 dispatch 状态。
_isHandled:通过 token 去检测一个函数是否被处理过了。
_isPending:通过 token 去检测一个函数是否被提交 Dispatcher 了。
_lastID:最近一次被加入 Dispatcher 的函数体的唯一标识,即 DispatchToken。
_pendingPayload:需要传递给调用函数的参数。
register 方法
register(callback: (payload: TPayload) => void): DispatchToken {
var id = _prefix + this._lastID++;
this._callbacks[id] = callback;
return id;
}
register 逻辑极其简单,首先根据之前的一些变量获取注册的回调函数的唯一标识(DispatchToken) _prefix + this._lastID++
对于 JavaScript 这样的单线程语言,这无疑是最高效安全的方法了。然后以这个唯一标识为 key ,回调函数为 value 保存到了 _callbacks 静态对象里面。最后返回了这个唯一标识。
unregister
有注册方法就有取消注册的方法!
unregister(id: DispatchToken): void {
invariant(
this._callbacks[id],
'Dispatcher.unregister(...): `%s` does not map to a registered callback.',
id
);
delete this._callbacks[id];
}
调用取消注册方法时需要传入需要取消注册回调函数的 DispatchToken,并对逻辑处理做了异常处理,最后使用 delete 直接删除了以 DispatchToken 为 key 的属性值。
dispatch
dispatch(payload: TPayload): void {
invariant(
!this._isDispatching,
'Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch.'
);
this._startDispatching(payload);
try {
for (var id in this._callbacks) {
if (this._isPending[id]) {
continue;
}
this._invokeCallback(id);
}
} finally {
this._stopDispatching();
}
}
整个函数的逻辑就是:首先是判断当前 Dispatcher 是否已经处于 Dispatching 状态中了,如果是,不能打断。然后通过 _startDispatching 更新状态。更新状态结束以后,将非 pending 状态的 callback 通过 _invokeCallback 执行( pending 在这里的含义可以简单理解为:正在执行或者锁死)。所有执行完了以后,通过 _stopDispatching 恢复状态。
isDispatching
返回当前 Dispatcher 是否在 dispatching 状态。
isDispatching(): boolean {
return this._isDispatching;
}
startDispatching
首先该函数将所有注册的 callback 的状态都清空,将传入的 action 赋给静态变量,表示正在执行的 action, 并标记 Dispatcher 的状态进入 dispatching。
_startDispatching(payload: TPayload): void {
for (var id in this._callbacks) {
this._isPending[id] = false;
this._isHandled[id] = false;
}
this._pendingPayload = payload;
this._isDispatching = true;
}
stopDispatching
删除静态变量存储的 action 的值,然后将 Dispatcher 的状态退出 dispatching。
_stopDispatching(): void {
delete this._pendingPayload;
this._isDispatching = false;
}
invokeCallback
_invokeCallback(id: DispatchToken): void {
this._isPending[id] = true;
this._callbacks[id](this._pendingPayload);
this._isHandled[id] = true;
}
当真正调用 callback 之前将其状态设置为 pending ,执行完成之后设置为handled。
waitFor
waitFor(ids: Array<DispatchToken>): void {
invariant(
this._isDispatching,
'Dispatcher.waitFor(...): Must be invoked while dispatching.'
);
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);
}
}
首先是用一个 Invariant 函数判断当前必须处于 Dispatching 状态。如果不处于Dispatching状态中,那么说明当前没有函数正在执行。
然后对 DispatchToken 数组进行遍历,如果遍历到的 DispatchToken 处于 pending 状态,就跳过,继续执行下一个遍历。
但是在这里有个「循环依赖」需要进行检查。试想如下情况:如果 A函数依赖 B函数的 Token, 同时 B函数依赖 A函数的 Token,这样就会造成「“死锁”」。所以,当一个函数依赖的对象处于 pending 状态时,说明这个函数已经开始执行了,但是如果同时该函数没有进入 handled 状态,说明该函数也被锁死了。
检查 token 对应的 callback 是否存在。
调用这个 token 对应的函数。
可以看出,waitFor 就是一个等待函数,当 B函数执行时,执行到某些条件不满足的时候(我们称之为依赖没解决),就是等待依赖去完成。有点类似下面的函数:
while (!satisfied) ;
doSomething
总结
这一溜下来,思路还是非常清晰的,没有什么复杂的地方。Dispatcher.js 源代码文件注释也很清晰,基本上是每个函数都有注释。
看完代码还是很有启发性的。之前一直觉得看源码是一件很痛苦的事情,知道看了 Dispatcher.js 源代码。学习继续,好好加油!