소스 검색

feat: Allow action subscribers to catch rejections. (#1740)

close #1489 #1558 #1625 

Co-authored-by: Joe Plahitko <joseph.plahitko@egov.com>
jmplahitko 5 년 전
부모
커밋
6ebbe64c58
4개의 변경된 파일123개의 추가작업 그리고 11개의 파일을 삭제
  1. 25 11
      src/store.js
  2. 40 0
      test/unit/modules.spec.js
  3. 2 0
      types/index.d.ts
  4. 56 0
      types/test/index.ts

+ 25 - 11
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)
+      })
     })
   }
 

+ 40 - 0
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({

+ 2 - 0
types/index.d.ts

@@ -77,10 +77,12 @@ export interface SubscribeOptions {
 }
 
 export type ActionSubscriber<P, S> = (action: P, state: S) => any;
+export type ActionErrorSubscriber<P, S> = (action: P, state: S, error: Error) => any;
 
 export interface ActionSubscribersObject<P, S> {
   before?: ActionSubscriber<P, S>;
   after?: ActionSubscriber<P, S>;
+  error?: ActionErrorSubscriber<P, S>;
 }
 
 export type SubscribeActionOptions<P, S> = ActionSubscriber<P, S> | ActionSubscribersObject<P, S>;

+ 56 - 0
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;
     }
   });