feat: error handling in scheduler

This commit is contained in:
Evan You 2019-08-30 15:15:23 -04:00
parent 966d7b5487
commit 09593c94c3
5 changed files with 45 additions and 56 deletions

View File

@ -10,7 +10,7 @@ import { EMPTY_OBJ, isObject, isArray, isFunction } from '@vue/shared'
import { recordEffect } from './apiReactivity'
import { getCurrentInstance } from './component'
import {
UserExecutionContexts,
ErrorTypes,
callWithErrorHandling,
callWithAsyncErrorHandling
} from './errorHandling'
@ -92,22 +92,14 @@ function doWatch(
s =>
isRef(s)
? s.value
: callWithErrorHandling(
s,
instance,
UserExecutionContexts.WATCH_GETTER
)
: callWithErrorHandling(s, instance, ErrorTypes.WATCH_GETTER)
)
} else if (isRef(source)) {
getter = () => source.value
} else if (cb) {
// getter with cb
getter = () =>
callWithErrorHandling(
source,
instance,
UserExecutionContexts.WATCH_GETTER
)
callWithErrorHandling(source, instance, ErrorTypes.WATCH_GETTER)
} else {
// no cb -> simple effect
getter = () => {
@ -117,7 +109,7 @@ function doWatch(
return callWithErrorHandling(
source,
instance,
UserExecutionContexts.WATCH_CALLBACK,
ErrorTypes.WATCH_CALLBACK,
[registerCleanup]
)
}
@ -132,7 +124,7 @@ function doWatch(
const registerCleanup: CleanupRegistrator = (fn: () => void) => {
// TODO wrap the cleanup fn for error handling
cleanup = runner.onStop = () => {
callWithErrorHandling(fn, instance, UserExecutionContexts.WATCH_CLEANUP)
callWithErrorHandling(fn, instance, ErrorTypes.WATCH_CLEANUP)
}
}
@ -145,12 +137,11 @@ function doWatch(
if (cleanup) {
cleanup()
}
callWithAsyncErrorHandling(
cb,
instance,
UserExecutionContexts.WATCH_CALLBACK,
[newValue, oldValue, registerCleanup]
)
callWithAsyncErrorHandling(cb, instance, ErrorTypes.WATCH_CALLBACK, [
newValue,
oldValue,
registerCleanup
])
oldValue = newValue
}
}

View File

@ -8,7 +8,7 @@ import { PatchFlags } from './patchFlags'
import { ShapeFlags } from './shapeFlags'
import { warn } from './warning'
import {
UserExecutionContexts,
ErrorTypes,
handleError,
callWithErrorHandling,
callWithAsyncErrorHandling
@ -238,7 +238,7 @@ export function createComponentInstance(
callWithAsyncErrorHandling(
handler[i],
instance,
UserExecutionContexts.COMPONENT_EVENT_HANDLER,
ErrorTypes.COMPONENT_EVENT_HANDLER,
args
)
}
@ -246,7 +246,7 @@ export function createComponentInstance(
callWithAsyncErrorHandling(
handler,
instance,
UserExecutionContexts.COMPONENT_EVENT_HANDLER,
ErrorTypes.COMPONENT_EVENT_HANDLER,
args
)
}
@ -287,7 +287,7 @@ export function setupStatefulComponent(instance: ComponentInstance) {
const setupResult = callWithErrorHandling(
setup,
instance,
UserExecutionContexts.SETUP_FUNCTION,
ErrorTypes.SETUP_FUNCTION,
[propsProxy, setupContext]
)
currentInstance = null
@ -382,7 +382,7 @@ export function renderComponentRoot(instance: ComponentInstance): VNode {
)
}
} catch (err) {
handleError(err, instance, UserExecutionContexts.RENDER_FUNCTION)
handleError(err, instance, ErrorTypes.RENDER_FUNCTION)
return createVNode(Empty)
}
}

View File

@ -4,7 +4,7 @@ import { warn, pushWarningContext, popWarningContext } from './warning'
// contexts where user provided function may be executed, in addition to
// lifecycle hooks.
export const enum UserExecutionContexts {
export const enum ErrorTypes {
SETUP_FUNCTION = 1,
RENDER_FUNCTION,
WATCH_GETTER,
@ -29,24 +29,24 @@ export const ErrorTypeStrings: Record<number | string, string> = {
[LifecycleHooks.ERROR_CAPTURED]: 'errorCaptured hook',
[LifecycleHooks.RENDER_TRACKED]: 'renderTracked hook',
[LifecycleHooks.RENDER_TRIGGERED]: 'renderTriggered hook',
[UserExecutionContexts.SETUP_FUNCTION]: 'setup function',
[UserExecutionContexts.RENDER_FUNCTION]: 'render function',
[UserExecutionContexts.WATCH_GETTER]: 'watcher getter',
[UserExecutionContexts.WATCH_CALLBACK]: 'watcher callback',
[UserExecutionContexts.WATCH_CLEANUP]: 'watcher cleanup function',
[UserExecutionContexts.NATIVE_EVENT_HANDLER]: 'native event handler',
[UserExecutionContexts.COMPONENT_EVENT_HANDLER]: 'component event handler',
[UserExecutionContexts.SCHEDULER]:
[ErrorTypes.SETUP_FUNCTION]: 'setup function',
[ErrorTypes.RENDER_FUNCTION]: 'render function',
[ErrorTypes.WATCH_GETTER]: 'watcher getter',
[ErrorTypes.WATCH_CALLBACK]: 'watcher callback',
[ErrorTypes.WATCH_CLEANUP]: 'watcher cleanup function',
[ErrorTypes.NATIVE_EVENT_HANDLER]: 'native event handler',
[ErrorTypes.COMPONENT_EVENT_HANDLER]: 'component event handler',
[ErrorTypes.SCHEDULER]:
'scheduler flush. This may be a Vue internals bug. ' +
'Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/vue'
}
type ErrorTypes = LifecycleHooks | UserExecutionContexts
type AllErrorTypes = LifecycleHooks | ErrorTypes
export function callWithErrorHandling(
fn: Function,
instance: ComponentInstance | null,
type: ErrorTypes,
type: AllErrorTypes,
args?: any[]
) {
let res: any
@ -61,7 +61,7 @@ export function callWithErrorHandling(
export function callWithAsyncErrorHandling(
fn: Function,
instance: ComponentInstance | null,
type: ErrorTypes,
type: AllErrorTypes,
args?: any[]
) {
const res = callWithErrorHandling(fn, instance, type, args)
@ -76,7 +76,7 @@ export function callWithAsyncErrorHandling(
export function handleError(
err: Error,
instance: ComponentInstance | null,
type: ErrorTypes
type: AllErrorTypes
) {
const contextVNode = instance ? instance.vnode : null
let cur: ComponentInstance | null = instance && instance.parent
@ -100,7 +100,7 @@ export function handleError(
logError(err, type, contextVNode)
}
function logError(err: Error, type: ErrorTypes, contextVNode: VNode | null) {
function logError(err: Error, type: AllErrorTypes, contextVNode: VNode | null) {
if (__DEV__) {
const info = ErrorTypeStrings[type]
if (contextVNode) {

View File

@ -1,3 +1,5 @@
import { handleError, ErrorTypes } from './errorHandling'
const queue: Function[] = []
const postFlushCbs: Function[] = []
const p = Promise.resolve()
@ -8,31 +10,23 @@ export function nextTick(fn?: () => void): Promise<void> {
return fn ? p.then(fn) : p
}
type ErrorHandler = (err: Error) => void
export function queueJob(job: () => void, onError?: ErrorHandler) {
export function queueJob(job: () => void) {
if (queue.indexOf(job) === -1) {
queue.push(job)
queueFlush(onError)
if (!isFlushing) {
nextTick(flushJobs)
}
}
}
export function queuePostFlushCb(
cb: Function | Function[],
onError?: ErrorHandler
) {
export function queuePostFlushCb(cb: Function | Function[]) {
if (Array.isArray(cb)) {
postFlushCbs.push.apply(postFlushCbs, cb)
} else {
postFlushCbs.push(cb)
}
queueFlush(onError)
}
function queueFlush(onError?: ErrorHandler) {
if (!isFlushing) {
const p = nextTick(flushJobs)
if (onError) p.catch(onError)
nextTick(flushJobs)
}
}
@ -75,7 +69,11 @@ function flushJobs(seenJobs?: JobCountMap) {
}
}
}
job()
try {
job()
} catch (err) {
handleError(err, null, ErrorTypes.SCHEDULER)
}
}
flushPostFlushCbs()
isFlushing = false

View File

@ -3,7 +3,7 @@ import {
ComponentInstance,
callWithAsyncErrorHandling
} from '@vue/runtime-core'
import { UserExecutionContexts } from 'packages/runtime-core/src/errorHandling'
import { ErrorTypes } from 'packages/runtime-core/src/errorHandling'
interface Invoker extends Function {
value: EventValue
@ -77,7 +77,7 @@ function createInvoker(value: any, instance: ComponentInstance | null) {
callWithAsyncErrorHandling(
value[i],
instance,
UserExecutionContexts.NATIVE_EVENT_HANDLER,
ErrorTypes.NATIVE_EVENT_HANDLER,
args
)
}
@ -85,7 +85,7 @@ function createInvoker(value: any, instance: ComponentInstance | null) {
callWithAsyncErrorHandling(
value,
instance,
UserExecutionContexts.NATIVE_EVENT_HANDLER,
ErrorTypes.NATIVE_EVENT_HANDLER,
args
)
}