Kaynağa Gözat

refactor: extract async error handling logic

Evan You 7 yıl önce
ebeveyn
işleme
b00868c5cb

+ 3 - 8
src/core/instance/events.js

@@ -4,9 +4,8 @@ import {
   tip,
   toArray,
   hyphenate,
-  handleError,
   formatComponentName,
-  handlePromiseError
+  invokeWithErrorHandling
 } from '../util/index'
 import { updateListeners } from '../vdom/helpers/index'
 
@@ -134,13 +133,9 @@ export function eventsMixin (Vue: Class<Component>) {
     if (cbs) {
       cbs = cbs.length > 1 ? toArray(cbs) : cbs
       const args = toArray(arguments, 1)
+      const info = `event handler for "${event}"`
       for (let i = 0, l = cbs.length; i < l; i++) {
-        try {
-          const cbResult = cbs[i].apply(vm, args)
-          handlePromiseError(cbResult, vm, `event handler for "${event}"`)
-        } catch (e) {
-          handleError(e, vm, `event handler for "${event}"`)
-        }
+        invokeWithErrorHandling(cbs[i], vm, args, vm, info)
       }
     }
     return vm

+ 3 - 8
src/core/instance/lifecycle.js

@@ -13,10 +13,9 @@ import {
   warn,
   noop,
   remove,
-  handleError,
   emptyObject,
   validateProp,
-  handlePromiseError
+  invokeWithErrorHandling
 } from '../util/index'
 
 export let activeInstance: any = null
@@ -324,14 +323,10 @@ export function callHook (vm: Component, hook: string) {
   // #7573 disable dep collection when invoking lifecycle hooks
   pushTarget()
   const handlers = vm.$options[hook]
+  const info = `${hook} hook`
   if (handlers) {
     for (let i = 0, j = handlers.length; i < j; i++) {
-      try {
-        const fnResult = handlers[i].call(vm)
-        handlePromiseError(fnResult, vm, `${hook} hook`)
-      } catch (e) {
-        handleError(e, vm, `${hook} hook`)
-      }
+      invokeWithErrorHandling(handlers[i], vm, null, vm, info)
     }
   }
   if (vm._hasHookEvent) {

+ 16 - 4
src/core/util/error.js

@@ -25,11 +25,23 @@ export function handleError (err: Error, vm: any, info: string) {
   globalHandleError(err, vm, info)
 }
 
-export function handlePromiseError (value: any, vm: any, info: string) {
-  // if value is promise, handle it (a promise must have a then function)
-  if (isPromise(value)) {
-    value.catch(e => handleError(e, vm, info))
+export function invokeWithErrorHandling (
+  handler: Function,
+  context: any,
+  args: null | any[],
+  vm: any,
+  info: string
+) {
+  let res
+  try {
+    res = args ? handler.apply(context, args) : handler.call(context)
+    if (isPromise(res)) {
+      res.catch(e => handleError(e, vm, info + ` (Promise/async)`))
+    }
+  } catch (e) {
+    handleError(e, vm, info)
   }
+  return res
 }
 
 function globalHandleError (err, vm, info) {

+ 6 - 15
src/core/vdom/helpers/update-listeners.js

@@ -1,6 +1,9 @@
 /* @flow */
 
-import { warn, handleError, handlePromiseError } from 'core/util/index'
+import {
+  warn,
+  invokeWithErrorHandling
+} from 'core/util/index'
 import {
   cached,
   isUndef,
@@ -36,23 +39,11 @@ export function createFnInvoker (fns: Function | Array<Function>, vm: ?Component
     if (Array.isArray(fns)) {
       const cloned = fns.slice()
       for (let i = 0; i < cloned.length; i++) {
-        try {
-          const result = cloned[i].apply(null, arguments)
-          handlePromiseError(result, vm, 'v-on async')
-        } catch (e) {
-          handleError(e, vm, 'v-on')
-        }
+        invokeWithErrorHandling(cloned[i], null, arguments, vm, `v-on handler`)
       }
     } else {
       // return handler return value for single handlers
-      let result
-      try {
-        result = fns.apply(null, arguments)
-        handlePromiseError(result, vm, 'v-on async')
-      } catch (e) {
-        handleError(e, vm, 'v-on')
-      }
-      return result
+      return invokeWithErrorHandling(fns, null, arguments, vm, `v-on handler`)
     }
   }
   invoker.fns = fns

+ 6 - 6
test/unit/features/error-handling.spec.js

@@ -33,7 +33,7 @@ describe('Error handling', () => {
     it(`should recover from promise errors in ${type}`, done => {
       createTestInstance(components[`${type}Async`])
       waitForUpdate(() => {
-        expect(`Error in ${description}`).toHaveBeenWarned()
+        expect(`Error in ${description} (Promise/async)`).toHaveBeenWarned()
         expect(`Error: ${type}`).toHaveBeenWarned()
       }).then(done)
     })
@@ -70,7 +70,7 @@ describe('Error handling', () => {
     it(`should recover from promise errors in ${type} hook`, done => {
       const vm = createTestInstance(components[`${type}Async`])
       assertBothInstancesActive(vm).then(() => {
-        expect(`Error in ${description}`).toHaveBeenWarned()
+        expect(`Error in ${description} (Promise/async)`).toHaveBeenWarned()
         expect(`Error: ${type}`).toHaveBeenWarned()
       }).then(done)
     })
@@ -101,7 +101,7 @@ describe('Error handling', () => {
       const vm = createTestInstance(components[`${type}Async`])
       vm.ok = false
       setTimeout(() => {
-        expect(`Error in ${description}`).toHaveBeenWarned()
+        expect(`Error in ${description} (Promise/async)`).toHaveBeenWarned()
         expect(`Error: ${type}`).toHaveBeenWarned()
         assertRootInstanceActive(vm).then(done)
       })
@@ -211,7 +211,7 @@ describe('Error handling', () => {
       }).$mount()
       document.body.appendChild(vm.$el)
       triggerEvent(vm.$el, 'click')
-      expect('Error in v-on').toHaveBeenWarned()
+      expect('Error in v-on handler').toHaveBeenWarned()
       expect('Error: v-on').toHaveBeenWarned()
       document.body.removeChild(vm.$el)
     })
@@ -226,8 +226,8 @@ describe('Error handling', () => {
       document.body.appendChild(vm.$el)
       triggerEvent(vm.$el, 'click')
       waitForUpdate(() => {
-        expect('Error in v-on async').toHaveBeenWarned()
-        expect('Error: v-on async').toHaveBeenWarned()
+        expect('Error in v-on handler (Promise/async)').toHaveBeenWarned()
+        expect('Error: v-on').toHaveBeenWarned()
         document.body.removeChild(vm.$el)
       }).then(done)
     })