refactor: extract async error handling logic

This commit is contained in:
Evan You 2018-12-20 10:26:56 -05:00
parent 35edc1c1e2
commit b00868c5cb
5 changed files with 34 additions and 41 deletions

View File

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

View File

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

View File

@ -25,11 +25,23 @@ export function handleError (err: Error, vm: any, info: string) {
globalHandleError(err, vm, info) globalHandleError(err, vm, info)
} }
export function handlePromiseError (value: any, vm: any, info: string) { export function invokeWithErrorHandling (
// if value is promise, handle it (a promise must have a then function) handler: Function,
if (isPromise(value)) { context: any,
value.catch(e => handleError(e, vm, info)) 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) { function globalHandleError (err, vm, info) {

View File

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

View File

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