diff --git a/packages/reactivity/src/computed.ts b/packages/reactivity/src/computed.ts index b16b011c5..628cf4636 100644 --- a/packages/reactivity/src/computed.ts +++ b/packages/reactivity/src/computed.ts @@ -84,9 +84,13 @@ export class ComputedRefImpl implements Subscriber { * @internal */ isSSR: boolean + /** + * @internal + */ + next?: Subscriber = undefined + // for backwards compat effect: this = this - // dev only onTrack?: (event: DebuggerEvent) => void // dev only diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 15693ee39..b32355081 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -261,13 +261,20 @@ export function endBatch(): void { while (batchedSub) { let e: Subscriber | undefined = batchedSub let next: Subscriber | undefined + // 1st pass: clear notified flags for computed upfront + // we use the ACTIVE flag as a discriminator between computed and effect, + // since NOTIFIED is useless for an inactive effect anyway. while (e) { - e.flags &= ~EffectFlags.NOTIFIED + if (!(e.flags & EffectFlags.ACTIVE)) { + e.flags &= ~EffectFlags.NOTIFIED + } e = e.next } e = batchedSub batchedSub = undefined + // 2nd pass: run effects while (e) { + e.flags &= ~EffectFlags.NOTIFIED if (e.flags & EffectFlags.ACTIVE) { try { // ACTIVE flag is effect-only diff --git a/packages/runtime-core/__tests__/apiWatch.spec.ts b/packages/runtime-core/__tests__/apiWatch.spec.ts index 082d585b8..10a4fe659 100644 --- a/packages/runtime-core/__tests__/apiWatch.spec.ts +++ b/packages/runtime-core/__tests__/apiWatch.spec.ts @@ -1930,7 +1930,7 @@ describe('api: watch', () => { warn.mockRestore() }) - it('should be executed correctly', () => { + test('should be executed correctly', () => { const v = ref(1) let foo = '' @@ -1957,4 +1957,30 @@ describe('api: watch', () => { v.value++ expect(foo).toBe('12') }) + + // 12045 + test('sync watcher should not break pre watchers', async () => { + const count1 = ref(0) + const count2 = ref(0) + + watch( + count1, + () => { + count2.value++ + }, + { flush: 'sync' }, + ) + + const spy1 = vi.fn() + watch([count1, count2], spy1) + + const spy2 = vi.fn() + watch(count1, spy2) + + count1.value++ + + await nextTick() + expect(spy1).toHaveBeenCalled() + expect(spy2).toHaveBeenCalled() + }) })