Browse Source

refactor: extract async error handling logic

dev
Evan You 6 years ago
parent
commit
b00868c5cb
  1. 11
      src/core/instance/events.js
  2. 11
      src/core/instance/lifecycle.js
  3. 20
      src/core/util/error.js
  4. 21
      src/core/vdom/helpers/update-listeners.js
  5. 12
      test/unit/features/error-handling.spec.js

11
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

11
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) {

20
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) {

21
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

12
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)
})

Loading…
Cancel
Save