refactor: sync value access for chained computed w/ scheduler

This commit is contained in:
Evan You
2021-07-08 00:31:26 -04:00
parent 02339b67d8
commit 1fe2239270
4 changed files with 132 additions and 41 deletions

View File

@@ -201,10 +201,29 @@ describe('reactivity/computed', () => {
})
describe('with scheduler', () => {
const p = Promise.resolve()
const defer = (fn?: any) => (fn ? p.then(fn) : p)
// a simple scheduler similar to the main Vue scheduler
const tick = Promise.resolve()
const queue: any[] = []
let queued = false
const schedule = (fn: any) => {
queue.push(fn)
if (!queued) {
queued = true
tick.then(flush)
}
}
const flush = () => {
for (let i = 0; i < queue.length; i++) {
queue[i]()
}
queue.length = 0
queued = false
}
beforeEach(() => {
setComputedScheduler(defer)
setComputedScheduler(schedule)
})
afterEach(() => {
@@ -224,7 +243,7 @@ describe('reactivity/computed', () => {
src.value = 3
// not called yet
expect(spy).toHaveBeenCalledTimes(1)
await defer()
await tick
// should only trigger once
expect(spy).toHaveBeenCalledTimes(2)
expect(spy).toHaveBeenCalledWith(c.value)
@@ -241,16 +260,67 @@ describe('reactivity/computed', () => {
src.value = 1
src.value = 2
await defer()
await tick
// should not trigger
expect(spy).toHaveBeenCalledTimes(1)
src.value = 3
src.value = 4
src.value = 5
await defer()
await tick
// should trigger because latest value changes
expect(spy).toHaveBeenCalledTimes(2)
})
test('chained computed value invalidation', async () => {
const effectSpy = jest.fn()
const c1Spy = jest.fn()
const c2Spy = jest.fn()
const src = ref(0)
const c1 = computed(() => {
c1Spy()
return src.value % 2
})
const c2 = computed(() => {
c2Spy()
return c1.value + 1
})
effect(() => {
effectSpy(c2.value)
})
expect(effectSpy).toHaveBeenCalledTimes(1)
expect(effectSpy).toHaveBeenCalledWith(1)
expect(c2.value).toBe(1)
expect(c1Spy).toHaveBeenCalledTimes(1)
expect(c2Spy).toHaveBeenCalledTimes(1)
src.value = 1
// value should be available sync
expect(c2.value).toBe(2)
expect(c2Spy).toHaveBeenCalledTimes(2)
await tick
expect(effectSpy).toHaveBeenCalledTimes(2)
expect(effectSpy).toHaveBeenCalledWith(2)
expect(c1Spy).toHaveBeenCalledTimes(2)
expect(c2Spy).toHaveBeenCalledTimes(2)
src.value = 2
await tick
expect(effectSpy).toHaveBeenCalledTimes(3)
expect(c1Spy).toHaveBeenCalledTimes(3)
expect(c2Spy).toHaveBeenCalledTimes(3)
src.value = 4
await tick
expect(effectSpy).toHaveBeenCalledTimes(3)
expect(c1Spy).toHaveBeenCalledTimes(4)
// in-between chained computed always re-compute, but it does avoid
// triggering the final subscribing effect.
expect(c2Spy).toHaveBeenCalledTimes(4)
})
})
})