diff --git a/src/vanilla/store.ts b/src/vanilla/store.ts index 73eb9fb06c..341f81a351 100644 --- a/src/vanilla/store.ts +++ b/src/vanilla/store.ts @@ -201,7 +201,7 @@ const registerBatchAtom = ( if (!batch.D.has(atom)) { batch.D.set(atom, new Set()) addBatchFuncMedium(batch, () => { - atomState.m?.l.forEach((listener) => listener()) + atomState.m?.l.forEach((listener) => addBatchFuncMedium(batch, listener)) }) } } @@ -220,12 +220,6 @@ const addBatchAtomDependent = ( const getBatchAtomDependents = (batch: Batch, atom: AnyAtom) => batch.D.get(atom) -const copySetAndClear = (origSet: Set): Set => { - const newSet = new Set(origSet) - origSet.clear() - return newSet -} - const flushBatch = (batch: Batch) => { let error: AnyError let hasError = false @@ -239,11 +233,14 @@ const flushBatch = (batch: Batch) => { } } } - while (batch.M.size || batch.L.size) { + while (batch.H.size || batch.M.size || batch.L.size) { batch.D.clear() - copySetAndClear(batch.H).forEach(call) - copySetAndClear(batch.M).forEach(call) - copySetAndClear(batch.L).forEach(call) + batch.H.forEach(call) + batch.H.clear() + batch.M.forEach(call) + batch.M.clear() + batch.L.forEach(call) + batch.L.clear() } if (hasError) { throw error diff --git a/tests/vanilla/store.test.tsx b/tests/vanilla/store.test.tsx index ea6a97e019..5a186b0a3f 100644 --- a/tests/vanilla/store.test.tsx +++ b/tests/vanilla/store.test.tsx @@ -983,3 +983,25 @@ it('mounted atom should be recomputed eagerly', () => { store.set(a, 1) expect(result).toEqual(['bRead', 'aCallback', 'bCallback']) }) + +it('should process all atom listeners even if some of them throw errors', () => { + const store = createStore() + const a = atom(0) + const listenerA = vi.fn() + const listenerB = vi.fn(() => { + throw new Error('error') + }) + const listenerC = vi.fn() + + store.sub(a, listenerA) + store.sub(a, listenerB) + store.sub(a, listenerC) + try { + store.set(a, 1) + } catch { + // expect empty + } + expect(listenerA).toHaveBeenCalledTimes(1) + expect(listenerB).toHaveBeenCalledTimes(1) + expect(listenerC).toHaveBeenCalledTimes(1) +})