We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
响应式中考虑如下的 case,为何可能会陷入死循环,以及如何解决?
it('should avoid implicit infinite recursive loops with itself', () => { const counter = reactive({ num: 0 }) effect(() => counter.num++) expect(counter.num).toBe(1) })
counter.num++ 实际可以写成这种展开形式
counter.num++
counter.num = counter.num + 1
意味着这条语句同时有 getter 和 setter,先 getter 后执行 setter。这时候应该有直觉会陷入死循环里了,所以我们可以开个 debug 看下。会发现执行顺序是:
getter
setter
effect
run()
run
() => counter.num++
track
trigger
从而发生了死循环。
分析结束可以得出结论,在当前的 activeEffect 正在执行的过程中,如果再次执行该 effect,即会陷入死循环。因此可以在 trigger 内部中判断当前的 effect 和 activeEffect 是否相等即可。
function triggerEffect(effect: any) { if (effect !== activeEffect) { // 添加这个判断以避免死循环 if (effect.scheduler) { effect.scheduler() } else { effect.run() } } }
在知道了避免死循环的方案后,我们按照尤大的方法继续把 case 变得更加严格:
it('should avoid implicit infinite recursive loops with itself', () => { const counter = reactive({ num: 0 }) const counterSpy = jest.fn(() => counter.num++) effect(counterSpy) expect(counter.num).toBe(1) expect(counterSpy).toHaveBeenCalledTimes(1) counter.num = 4 expect(counter.num).toBe(5) expect(counterSpy).toHaveBeenCalledTimes(2) })
你会发现按照崔大的写法 case 中的这句过不去了
expect(counter.num).toBe(5)
原因也很简单,我们忘记在 ReactiveEffect 的 run 方法中,在执行结束后清理现场了。否则 activeEffect 明明已经执行结束了,却还保留有值,导致新的 effect 无法进入。修改为下述代码即可
ReactiveEffect
run() { if (!this.active) { return this._fn() } shouldTrack = true activeEffect = this const result = this._fn() shouldTrack = false // 除了 shouldTrack 需要改为 false 外 // activeEffect 也需要清理现场为 undefined activeEffect = undefined return result }
响应式自增自减产生死循环的情况在 HCY 的书中和群里都有人提及,因此在这里写篇文章记录一下。
其实 activeEffect 这样写还是存在问题,因为如果只有一个 activeEffect,如果是嵌套的 effect 的话,就会丢失上下文环境,因此最好要写成栈的形式,我后续会继续补充。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
问题
响应式中考虑如下的 case,为何可能会陷入死循环,以及如何解决?
分析
counter.num++
实际可以写成这种展开形式意味着这条语句同时有
getter
和setter
,先getter
后执行setter
。这时候应该有直觉会陷入死循环里了,所以我们可以开个 debug 看下。会发现执行顺序是:effect
函数中执行run()
run
函数中调用 this.fn(),即() => counter.num++
getter
中执行track
,返回当前值setter
中执行trigger
trigger
中拿到 target -> key -> dep -> effect 实例 执行从而发生了死循环。
分析结束可以得出结论,在当前的 activeEffect 正在执行的过程中,如果再次执行该 effect,即会陷入死循环。因此可以在
trigger
内部中判断当前的 effect 和 activeEffect 是否相等即可。解决方案
继续深入
在知道了避免死循环的方案后,我们按照尤大的方法继续把 case 变得更加严格:
你会发现按照崔大的写法 case 中的这句过不去了
原因也很简单,我们忘记在
ReactiveEffect
的run
方法中,在执行结束后清理现场了。否则 activeEffect 明明已经执行结束了,却还保留有值,导致新的 effect 无法进入。修改为下述代码即可总结
响应式自增自减产生死循环的情况在 HCY 的书中和群里都有人提及,因此在这里写篇文章记录一下。
其实 activeEffect 这样写还是存在问题,因为如果只有一个 activeEffect,如果是嵌套的 effect 的话,就会丢失上下文环境,因此最好要写成栈的形式,我后续会继续补充。
The text was updated successfully, but these errors were encountered: