diff --git a/src/core/instance/events.js b/src/core/instance/events.js index fa2c8d4f..4d5bb552 100644 --- a/src/core/instance/events.js +++ b/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) { 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 diff --git a/src/core/instance/lifecycle.js b/src/core/instance/lifecycle.js index 5efa0c61..3879c7b5 100644 --- a/src/core/instance/lifecycle.js +++ b/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) { diff --git a/src/core/util/error.js b/src/core/util/error.js index 39a23650..ea4d0411 100644 --- a/src/core/util/error.js +++ b/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) { diff --git a/src/core/vdom/helpers/update-listeners.js b/src/core/vdom/helpers/update-listeners.js index 88cab4ec..dcde7f48 100644 --- a/src/core/vdom/helpers/update-listeners.js +++ b/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, 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 diff --git a/test/unit/features/error-handling.spec.js b/test/unit/features/error-handling.spec.js index 2b10ea63..7d0eaa51 100644 --- a/test/unit/features/error-handling.spec.js +++ b/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) })