feat: error handling in scheduler
This commit is contained in:
parent
966d7b5487
commit
09593c94c3
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user