From 6ebbe64c5821d19e55a41dc8b1d81cfce6cbd195 Mon Sep 17 00:00:00 2001 From: jmplahitko Date: Fri, 8 May 2020 04:29:08 -0400 Subject: [PATCH] feat: Allow action subscribers to catch rejections. (#1740) close #1489 #1558 #1625 Co-authored-by: Joe Plahitko --- src/store.js | 36 +++++++++++++++++-------- test/unit/modules.spec.js | 40 ++++++++++++++++++++++++++++ types/index.d.ts | 2 ++ types/test/index.ts | 56 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+), 11 deletions(-) diff --git a/src/store.js b/src/store.js index fcfcb98fb..03169d958 100644 --- a/src/store.js +++ b/src/store.js @@ -149,18 +149,32 @@ export class Store { ? Promise.all(entry.map(handler => handler(payload))) : entry[0](payload) - return result.then(res => { - try { - this._actionSubscribers - .filter(sub => sub.after) - .forEach(sub => sub.after(action, this.state)) - } catch (e) { - if (__DEV__) { - console.warn(`[vuex] error in after action subscribers: `) - console.error(e) + return new Promise((resolve, reject) => { + result.then(res => { + try { + this._actionSubscribers + .filter(sub => sub.after) + .forEach(sub => sub.after(action, this.state)) + } catch (e) { + if (__DEV__) { + console.warn(`[vuex] error in after action subscribers: `) + console.error(e) + } } - } - return res + resolve(res) + }, e => { + try { + this._actionSubscribers + .filter(sub => sub.error) + .forEach(sub => sub.error(action, this.state, e)) + } catch (_e) { + if (__DEV__) { + console.warn(`[vuex] error in error action subscribers: `) + console.error(_e) + } + } + reject(e) + }) }) } diff --git a/test/unit/modules.spec.js b/test/unit/modules.spec.js index 232b33f27..4f10df326 100644 --- a/test/unit/modules.spec.js +++ b/test/unit/modules.spec.js @@ -733,6 +733,46 @@ describe('Modules', () => { }) }) + it('action error subscribers', (done) => { + const beforeSpy = jasmine.createSpy() + const afterSpy = jasmine.createSpy() + const errorSpy = jasmine.createSpy() + const error = new Error() + const store = new Vuex.Store({ + actions: { + [TEST]: () => Promise.reject(error) + }, + plugins: [ + store => { + store.subscribeAction({ + before: beforeSpy, + after: afterSpy, + error: errorSpy + }) + } + ] + }) + store.dispatch(TEST, 2).catch(() => { + expect(beforeSpy).toHaveBeenCalledWith( + { type: TEST, payload: 2 }, + store.state + ) + expect(afterSpy).not.toHaveBeenCalled() + Vue.nextTick(() => { + expect(afterSpy).not.toHaveBeenCalledWith( + { type: TEST, payload: 2 }, + store.state + ) + expect(errorSpy).toHaveBeenCalledWith( + { type: TEST, payload: 2 }, + store.state, + error + ) + done() + }) + }) + }) + it('asserts a mutation should be a function', () => { expect(() => { new Vuex.Store({ diff --git a/types/index.d.ts b/types/index.d.ts index ef2a6a45b..46b928af7 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -77,10 +77,12 @@ export interface SubscribeOptions { } export type ActionSubscriber = (action: P, state: S) => any; +export type ActionErrorSubscriber = (action: P, state: S, error: Error) => any; export interface ActionSubscribersObject { before?: ActionSubscriber; after?: ActionSubscriber; + error?: ActionErrorSubscriber; } export type SubscribeActionOptions = ActionSubscriber | ActionSubscribersObject; diff --git a/types/test/index.ts b/types/test/index.ts index 8caac47cd..92e138cb9 100644 --- a/types/test/index.ts +++ b/types/test/index.ts @@ -68,11 +68,67 @@ namespace StoreInstance { } }); + store.subscribeAction({ + before(action, state) { + action.type; + action.payload; + state.value; + }, + error(action, state, error) { + action.type; + action.payload; + state.value; + error; + } + }); + + store.subscribeAction({ + before(action, state) { + action.type; + action.payload; + state.value; + }, + after(action, state) { + action.type; + action.payload; + state.value; + }, + error(action, state, error) { + action.type; + action.payload; + state.value; + error; + } + }); + + store.subscribeAction({ + after(action, state) { + action.type; + action.payload; + state.value; + } + }); + store.subscribeAction({ after(action, state) { action.type; action.payload; state.value; + }, + error(action, state, error) { + action.type; + action.payload; + state.value; + error; + } + }); + + store.subscribeAction({ + error(action, state, error) { + action.type; + action.payload; + state.value; + error; } });