diff --git a/dist/vuex.common.js b/dist/vuex.common.js index 88436c580..0b4aca688 100644 --- a/dist/vuex.common.js +++ b/dist/vuex.common.js @@ -1,6 +1,6 @@ /** * vuex v3.1.0 - * (c) 2019 Evan You + * (c) 2020 Evan You * @license MIT */ 'use strict'; @@ -41,9 +41,12 @@ function applyMixin (Vue) { } } -var devtoolHook = - typeof window !== 'undefined' && - window.__VUE_DEVTOOLS_GLOBAL_HOOK__; +var target = typeof window !== 'undefined' + ? window + : typeof global !== 'undefined' + ? global + : {}; +var devtoolHook = target.__VUE_DEVTOOLS_GLOBAL_HOOK__; function devtoolPlugin (store) { if (!devtoolHook) { return } @@ -429,18 +432,32 @@ Store.prototype.dispatch = function dispatch (_type, _payload) { ? Promise.all(entry.map(function (handler) { return handler(payload); })) : entry[0](payload); - return result.then(function (res) { - try { - this$1._actionSubscribers - .filter(function (sub) { return sub.after; }) - .forEach(function (sub) { return sub.after(action, this$1.state); }); - } catch (e) { - if (process.env.NODE_ENV !== 'production') { - console.warn("[vuex] error in after action subscribers: "); - console.error(e); + return new Promise(function (resolve, reject) { + result.then(function (res) { + try { + this$1._actionSubscribers + .filter(function (sub) { return sub.after; }) + .forEach(function (sub) { return sub.after(action, this$1.state); }); + } catch (e) { + if (process.env.NODE_ENV !== 'production') { + console.warn("[vuex] error in after action subscribers: "); + console.error(e); + } } - } - return res + resolve(res); + }, function (e) { + try { + this$1._actionSubscribers + .filter(function (sub) { return sub.catch; }) + .forEach(function (sub) { return sub.catch(action, this$1.state, e); }); + } catch (_e) { + if (process.env.NODE_ENV !== 'production') { + console.warn("[vuex] error in after action catch subscribers: "); + console.error(_e); + } + } + reject(e); + }); }) }; diff --git a/src/store.js b/src/store.js index dfcc755f8..49ac3a7b4 100644 --- a/src/store.js +++ b/src/store.js @@ -144,18 +144,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 (process.env.NODE_ENV !== 'production') { - 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 (process.env.NODE_ENV !== 'production') { + console.warn(`[vuex] error in after action subscribers: `) + console.error(e) + } } - } - return res + resolve(res) + }, e => { + try { + this._actionSubscribers + .filter(sub => sub.catch) + .forEach(sub => sub.catch(action, this.state, e)) + } catch (_e) { + if (process.env.NODE_ENV !== 'production') { + console.warn(`[vuex] error in after action catch subscribers: `) + console.error(_e) + } + } + reject(e) + }) }) } diff --git a/test/unit/modules.spec.js b/test/unit/modules.spec.js index a776fb582..1387ccf7a 100644 --- a/test/unit/modules.spec.js +++ b/test/unit/modules.spec.js @@ -701,6 +701,46 @@ describe('Modules', () => { }) }) + it('action catch subscribers', (done) => { + const beforeSpy = jasmine.createSpy() + const afterSpy = jasmine.createSpy() + const catchSpy = jasmine.createSpy() + const error = new Error() + const store = new Vuex.Store({ + actions: { + [TEST]: () => Promise.reject(error) + }, + plugins: [ + store => { + store.subscribeAction({ + before: beforeSpy, + after: afterSpy, + catch: catchSpy + }) + } + ] + }) + 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(catchSpy).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 3cd40067a..e569724cb 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -70,10 +70,12 @@ export interface ActionPayload extends Payload { } export type ActionSubscriber = (action: P, state: S) => any; +export type ActionCatchSubscriber = (action: P, state: S, error: Error) => any; export interface ActionSubscribersObject { before?: ActionSubscriber; after?: ActionSubscriber; + catch?: ActionCatchSubscriber; } export type SubscribeActionOptions = ActionSubscriber | ActionSubscribersObject; diff --git a/types/test/index.ts b/types/test/index.ts index d6af9a4fa..fd7bc4e6c 100644 --- a/types/test/index.ts +++ b/types/test/index.ts @@ -66,11 +66,67 @@ namespace StoreInstance { } }); + store.subscribeAction({ + before(action, state) { + action.type; + action.payload; + state.value; + }, + catch(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; + }, + catch(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; + }, + catch(action, state, error) { + action.type; + action.payload; + state.value; + error; + } + }); + + store.subscribeAction({ + catch(action, state, error) { + action.type; + action.payload; + state.value; + error; } });