阅读 169

Mobx 源码分析 - autorun

作用

当用 autorun 时,函数会被立即执行一次,然后每当它的依赖有任何改变,autorun 都会执行一次。

源码解析

  1. 生成 name

  2. 判断 opts 上是否有 schedulerdelay。如果都没有,则为 true,否则为 false

    • runSync 为 true

      1. 实例化 Reaction,传入 nameonInvalidate 函数和错误处理函数。

        reaction = new Reaction(
            name,
            function(this: Reaction) {
                this.track(reactionRunner);
            },
            opts.onError
        );
        复制代码
    • runSync 为 false

      1. 根据传入参数,生成自己的调度 scheduler 函数

      2. 实例化 Reaction,传入 nameonInvalidate 函数和错误处理函数。

        reaction = new Reaction(
            name,
            () => {
                if (!isScheduled) {
                    isScheduled = true;
                    scheduler(() => {
                        isScheduled = false;
                        if (!reaction.isDisposed) reaction.track(reactionRunner);
                    });
                }
            },
            opts.onError
        );
        复制代码
  3. this 添加到全局的待处理列表 pendingReactions

  4. 开始执行 runReactions

  5. 判断当前全局中是否处于处理事务状态或处理反应 reactions 阶段,如果是则返回,什么都不做。否则执行 reactionScheduler(runReactionsHelper),也就是执行 runReactionsHelper

    if (globalState.inBatch > 0 || globalState.isRunningReactions) return
        reactionScheduler(runReactionsHelper)
    复制代码
  6. 把全局状态中 isRunningReactions 设为 true,通过 pendingReactions 取到所有待处理 Reaction 实例,并清空 pendingReactions 列表

  7. 遍历所有实例,针对每一个实例都调用其自身的 runReaction,遍历结束后把全局 isRunningReactions 改为 false

        runReaction() {
            // 当前是否已经清除
            if (!this.isDisposed) {
                // 开始处理事务,设置 global.inBatch,令其值 +1
                startBatch()
                this._isScheduled = false
                // 判断是否需要追踪,当前 dependenciesState 处于 NOT_TRACKING,shouldCompute 会对于此状态返回 true
                if (shouldCompute(this)) {
                    // 改变当前状态
                    this._isTrackPending = true
    
                    try {
                        // 执行传递进来的函数
                        this.onInvalidate()
                        // 判断当前 _isTrackPending 状态和全局监听器 spy,如果有全局监听器,则发送事件,类型为 scheduled-reaction
                        if (this._isTrackPending && isSpyEnabled()) {
                            // onInvalidate didn't trigger track right away..
                            spyReport({
                                name: this.name,
                                type: "scheduled-reaction"
                            })
                        }
                    } catch (e) {
                        // 错误处理
                        this.reportExceptionInDerivation(e)
                    }
                }
                // 结束处理事务
                endBatch()
            }
        }
    复制代码

onInvalidate

执行 track 函数,参数为 reactionRunner

// api/autorun.ts
function(this: Reaction) {
    this.track(reactionRunner);
}
// core/reaction.ts
function track(fn){
    startBatch()
    ...
    const result = trackDerivedFunction(this, fn, undefined)
    ...
}
// core/derivation.ts
function trackDerivedFunction(derivation, f, context){
    ...
    result = f.call(context)
    ...
    bindDependencies(derivation)
}
复制代码
  1. 函数内部会调用 startBatch,再次使 global.inBatch+1
  2. 判断是否有监听器 spy,有则发送事件,类型为 reaction,并记录当前时间
  3. 设置当前状态 _isRunningtrue
  4. 改变当前依赖状态 dependenciesStateUP_TO_DATEUP_TO_DATE 意味着值是新的
  5. 取出传入对象的 observing 属性值,如果有值,则遍历,把每一项的 lowestObserverState 都设为 UP_TO_DATE
  6. 把传入的 derivation 设置到全局对象上,同时给 derivation 新增属性
  7. 判断是否需要捕获错误 globalState.disableErrorBoundaries,调用传入的函数,收集新的依赖
  8. 把全局对象 trackingDerivation 恢复原状
  9. 更新依赖关系,针对新的依赖,增加监听者,对于旧的依赖且新的依赖中也没有使用到的,则移除此依赖,并执行 onBecomeStale,更新所有的依赖项
  10. 根据 this.isDisposed 判断需要清除 autorun
  11. 如有监听器,则向监听器发送此次 reactionderivation 耗时
  12. 结束事务处理,移除监听者,并把清除监听状态