diff --git a/src/store.js b/src/store.js index c4cb3f1a5..8b89d8187 100644 --- a/src/store.js +++ b/src/store.js @@ -101,7 +101,10 @@ export class Store { handler(payload) }) }) - this._subscribers.forEach(sub => sub(mutation, this.state)) + + this._subscribers + .slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe + .forEach(sub => sub(mutation, this.state)) if ( process.env.NODE_ENV !== 'production' && @@ -132,6 +135,7 @@ export class Store { try { this._actionSubscribers + .slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe .filter(sub => sub.before) .forEach(sub => sub.before(action, this.state)) } catch (e) { diff --git a/test/unit/store.spec.js b/test/unit/store.spec.js index 001ee4249..561496e60 100644 --- a/test/unit/store.spec.js +++ b/test/unit/store.spec.js @@ -320,6 +320,48 @@ describe('Store', () => { expect(secondSubscribeSpy.calls.count()).toBe(2) }) + it('subscribe: should handle subscriptions with synchronous unsubscriptions', () => { + const subscribeSpy = jasmine.createSpy() + const testPayload = 2 + const store = new Vuex.Store({ + state: {}, + mutations: { + [TEST]: () => {} + } + }) + + const unsubscribe = store.subscribe(() => unsubscribe()) + store.subscribe(subscribeSpy) + store.commit(TEST, testPayload) + + expect(subscribeSpy).toHaveBeenCalledWith( + { type: TEST, payload: testPayload }, + store.state + ) + expect(subscribeSpy.calls.count()).toBe(1) + }) + + it('subscribeAction: should handle subscriptions with synchronous unsubscriptions', () => { + const subscribeSpy = jasmine.createSpy() + const testPayload = 2 + const store = new Vuex.Store({ + state: {}, + actions: { + [TEST]: () => {} + } + }) + + const unsubscribe = store.subscribeAction(() => unsubscribe()) + store.subscribeAction(subscribeSpy) + store.dispatch(TEST, testPayload) + + expect(subscribeSpy).toHaveBeenCalledWith( + { type: TEST, payload: testPayload }, + store.state + ) + expect(subscribeSpy.calls.count()).toBe(1) + }) + // store.watch should only be asserted in non-SSR environment if (!isSSR) { it('strict mode: warn mutations outside of handlers', () => {