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 { recordEffect } from './apiReactivity'
|
||||||
import { getCurrentInstance } from './component'
|
import { getCurrentInstance } from './component'
|
||||||
import {
|
import {
|
||||||
UserExecutionContexts,
|
ErrorTypes,
|
||||||
callWithErrorHandling,
|
callWithErrorHandling,
|
||||||
callWithAsyncErrorHandling
|
callWithAsyncErrorHandling
|
||||||
} from './errorHandling'
|
} from './errorHandling'
|
||||||
@ -92,22 +92,14 @@ function doWatch(
|
|||||||
s =>
|
s =>
|
||||||
isRef(s)
|
isRef(s)
|
||||||
? s.value
|
? s.value
|
||||||
: callWithErrorHandling(
|
: callWithErrorHandling(s, instance, ErrorTypes.WATCH_GETTER)
|
||||||
s,
|
|
||||||
instance,
|
|
||||||
UserExecutionContexts.WATCH_GETTER
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
} else if (isRef(source)) {
|
} else if (isRef(source)) {
|
||||||
getter = () => source.value
|
getter = () => source.value
|
||||||
} else if (cb) {
|
} else if (cb) {
|
||||||
// getter with cb
|
// getter with cb
|
||||||
getter = () =>
|
getter = () =>
|
||||||
callWithErrorHandling(
|
callWithErrorHandling(source, instance, ErrorTypes.WATCH_GETTER)
|
||||||
source,
|
|
||||||
instance,
|
|
||||||
UserExecutionContexts.WATCH_GETTER
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
// no cb -> simple effect
|
// no cb -> simple effect
|
||||||
getter = () => {
|
getter = () => {
|
||||||
@ -117,7 +109,7 @@ function doWatch(
|
|||||||
return callWithErrorHandling(
|
return callWithErrorHandling(
|
||||||
source,
|
source,
|
||||||
instance,
|
instance,
|
||||||
UserExecutionContexts.WATCH_CALLBACK,
|
ErrorTypes.WATCH_CALLBACK,
|
||||||
[registerCleanup]
|
[registerCleanup]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -132,7 +124,7 @@ function doWatch(
|
|||||||
const registerCleanup: CleanupRegistrator = (fn: () => void) => {
|
const registerCleanup: CleanupRegistrator = (fn: () => void) => {
|
||||||
// TODO wrap the cleanup fn for error handling
|
// TODO wrap the cleanup fn for error handling
|
||||||
cleanup = runner.onStop = () => {
|
cleanup = runner.onStop = () => {
|
||||||
callWithErrorHandling(fn, instance, UserExecutionContexts.WATCH_CLEANUP)
|
callWithErrorHandling(fn, instance, ErrorTypes.WATCH_CLEANUP)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,12 +137,11 @@ function doWatch(
|
|||||||
if (cleanup) {
|
if (cleanup) {
|
||||||
cleanup()
|
cleanup()
|
||||||
}
|
}
|
||||||
callWithAsyncErrorHandling(
|
callWithAsyncErrorHandling(cb, instance, ErrorTypes.WATCH_CALLBACK, [
|
||||||
cb,
|
newValue,
|
||||||
instance,
|
oldValue,
|
||||||
UserExecutionContexts.WATCH_CALLBACK,
|
registerCleanup
|
||||||
[newValue, oldValue, registerCleanup]
|
])
|
||||||
)
|
|
||||||
oldValue = newValue
|
oldValue = newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import { PatchFlags } from './patchFlags'
|
|||||||
import { ShapeFlags } from './shapeFlags'
|
import { ShapeFlags } from './shapeFlags'
|
||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
import {
|
import {
|
||||||
UserExecutionContexts,
|
ErrorTypes,
|
||||||
handleError,
|
handleError,
|
||||||
callWithErrorHandling,
|
callWithErrorHandling,
|
||||||
callWithAsyncErrorHandling
|
callWithAsyncErrorHandling
|
||||||
@ -238,7 +238,7 @@ export function createComponentInstance(
|
|||||||
callWithAsyncErrorHandling(
|
callWithAsyncErrorHandling(
|
||||||
handler[i],
|
handler[i],
|
||||||
instance,
|
instance,
|
||||||
UserExecutionContexts.COMPONENT_EVENT_HANDLER,
|
ErrorTypes.COMPONENT_EVENT_HANDLER,
|
||||||
args
|
args
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -246,7 +246,7 @@ export function createComponentInstance(
|
|||||||
callWithAsyncErrorHandling(
|
callWithAsyncErrorHandling(
|
||||||
handler,
|
handler,
|
||||||
instance,
|
instance,
|
||||||
UserExecutionContexts.COMPONENT_EVENT_HANDLER,
|
ErrorTypes.COMPONENT_EVENT_HANDLER,
|
||||||
args
|
args
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -287,7 +287,7 @@ export function setupStatefulComponent(instance: ComponentInstance) {
|
|||||||
const setupResult = callWithErrorHandling(
|
const setupResult = callWithErrorHandling(
|
||||||
setup,
|
setup,
|
||||||
instance,
|
instance,
|
||||||
UserExecutionContexts.SETUP_FUNCTION,
|
ErrorTypes.SETUP_FUNCTION,
|
||||||
[propsProxy, setupContext]
|
[propsProxy, setupContext]
|
||||||
)
|
)
|
||||||
currentInstance = null
|
currentInstance = null
|
||||||
@ -382,7 +382,7 @@ export function renderComponentRoot(instance: ComponentInstance): VNode {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
handleError(err, instance, UserExecutionContexts.RENDER_FUNCTION)
|
handleError(err, instance, ErrorTypes.RENDER_FUNCTION)
|
||||||
return createVNode(Empty)
|
return createVNode(Empty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import { warn, pushWarningContext, popWarningContext } from './warning'
|
|||||||
|
|
||||||
// contexts where user provided function may be executed, in addition to
|
// contexts where user provided function may be executed, in addition to
|
||||||
// lifecycle hooks.
|
// lifecycle hooks.
|
||||||
export const enum UserExecutionContexts {
|
export const enum ErrorTypes {
|
||||||
SETUP_FUNCTION = 1,
|
SETUP_FUNCTION = 1,
|
||||||
RENDER_FUNCTION,
|
RENDER_FUNCTION,
|
||||||
WATCH_GETTER,
|
WATCH_GETTER,
|
||||||
@ -29,24 +29,24 @@ export const ErrorTypeStrings: Record<number | string, string> = {
|
|||||||
[LifecycleHooks.ERROR_CAPTURED]: 'errorCaptured hook',
|
[LifecycleHooks.ERROR_CAPTURED]: 'errorCaptured hook',
|
||||||
[LifecycleHooks.RENDER_TRACKED]: 'renderTracked hook',
|
[LifecycleHooks.RENDER_TRACKED]: 'renderTracked hook',
|
||||||
[LifecycleHooks.RENDER_TRIGGERED]: 'renderTriggered hook',
|
[LifecycleHooks.RENDER_TRIGGERED]: 'renderTriggered hook',
|
||||||
[UserExecutionContexts.SETUP_FUNCTION]: 'setup function',
|
[ErrorTypes.SETUP_FUNCTION]: 'setup function',
|
||||||
[UserExecutionContexts.RENDER_FUNCTION]: 'render function',
|
[ErrorTypes.RENDER_FUNCTION]: 'render function',
|
||||||
[UserExecutionContexts.WATCH_GETTER]: 'watcher getter',
|
[ErrorTypes.WATCH_GETTER]: 'watcher getter',
|
||||||
[UserExecutionContexts.WATCH_CALLBACK]: 'watcher callback',
|
[ErrorTypes.WATCH_CALLBACK]: 'watcher callback',
|
||||||
[UserExecutionContexts.WATCH_CLEANUP]: 'watcher cleanup function',
|
[ErrorTypes.WATCH_CLEANUP]: 'watcher cleanup function',
|
||||||
[UserExecutionContexts.NATIVE_EVENT_HANDLER]: 'native event handler',
|
[ErrorTypes.NATIVE_EVENT_HANDLER]: 'native event handler',
|
||||||
[UserExecutionContexts.COMPONENT_EVENT_HANDLER]: 'component event handler',
|
[ErrorTypes.COMPONENT_EVENT_HANDLER]: 'component event handler',
|
||||||
[UserExecutionContexts.SCHEDULER]:
|
[ErrorTypes.SCHEDULER]:
|
||||||
'scheduler flush. This may be a Vue internals bug. ' +
|
'scheduler flush. This may be a Vue internals bug. ' +
|
||||||
'Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/vue'
|
'Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/vue'
|
||||||
}
|
}
|
||||||
|
|
||||||
type ErrorTypes = LifecycleHooks | UserExecutionContexts
|
type AllErrorTypes = LifecycleHooks | ErrorTypes
|
||||||
|
|
||||||
export function callWithErrorHandling(
|
export function callWithErrorHandling(
|
||||||
fn: Function,
|
fn: Function,
|
||||||
instance: ComponentInstance | null,
|
instance: ComponentInstance | null,
|
||||||
type: ErrorTypes,
|
type: AllErrorTypes,
|
||||||
args?: any[]
|
args?: any[]
|
||||||
) {
|
) {
|
||||||
let res: any
|
let res: any
|
||||||
@ -61,7 +61,7 @@ export function callWithErrorHandling(
|
|||||||
export function callWithAsyncErrorHandling(
|
export function callWithAsyncErrorHandling(
|
||||||
fn: Function,
|
fn: Function,
|
||||||
instance: ComponentInstance | null,
|
instance: ComponentInstance | null,
|
||||||
type: ErrorTypes,
|
type: AllErrorTypes,
|
||||||
args?: any[]
|
args?: any[]
|
||||||
) {
|
) {
|
||||||
const res = callWithErrorHandling(fn, instance, type, args)
|
const res = callWithErrorHandling(fn, instance, type, args)
|
||||||
@ -76,7 +76,7 @@ export function callWithAsyncErrorHandling(
|
|||||||
export function handleError(
|
export function handleError(
|
||||||
err: Error,
|
err: Error,
|
||||||
instance: ComponentInstance | null,
|
instance: ComponentInstance | null,
|
||||||
type: ErrorTypes
|
type: AllErrorTypes
|
||||||
) {
|
) {
|
||||||
const contextVNode = instance ? instance.vnode : null
|
const contextVNode = instance ? instance.vnode : null
|
||||||
let cur: ComponentInstance | null = instance && instance.parent
|
let cur: ComponentInstance | null = instance && instance.parent
|
||||||
@ -100,7 +100,7 @@ export function handleError(
|
|||||||
logError(err, type, contextVNode)
|
logError(err, type, contextVNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
function logError(err: Error, type: ErrorTypes, contextVNode: VNode | null) {
|
function logError(err: Error, type: AllErrorTypes, contextVNode: VNode | null) {
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
const info = ErrorTypeStrings[type]
|
const info = ErrorTypeStrings[type]
|
||||||
if (contextVNode) {
|
if (contextVNode) {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { handleError, ErrorTypes } from './errorHandling'
|
||||||
|
|
||||||
const queue: Function[] = []
|
const queue: Function[] = []
|
||||||
const postFlushCbs: Function[] = []
|
const postFlushCbs: Function[] = []
|
||||||
const p = Promise.resolve()
|
const p = Promise.resolve()
|
||||||
@ -8,31 +10,23 @@ export function nextTick(fn?: () => void): Promise<void> {
|
|||||||
return fn ? p.then(fn) : p
|
return fn ? p.then(fn) : p
|
||||||
}
|
}
|
||||||
|
|
||||||
type ErrorHandler = (err: Error) => void
|
export function queueJob(job: () => void) {
|
||||||
|
|
||||||
export function queueJob(job: () => void, onError?: ErrorHandler) {
|
|
||||||
if (queue.indexOf(job) === -1) {
|
if (queue.indexOf(job) === -1) {
|
||||||
queue.push(job)
|
queue.push(job)
|
||||||
queueFlush(onError)
|
if (!isFlushing) {
|
||||||
|
nextTick(flushJobs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function queuePostFlushCb(
|
export function queuePostFlushCb(cb: Function | Function[]) {
|
||||||
cb: Function | Function[],
|
|
||||||
onError?: ErrorHandler
|
|
||||||
) {
|
|
||||||
if (Array.isArray(cb)) {
|
if (Array.isArray(cb)) {
|
||||||
postFlushCbs.push.apply(postFlushCbs, cb)
|
postFlushCbs.push.apply(postFlushCbs, cb)
|
||||||
} else {
|
} else {
|
||||||
postFlushCbs.push(cb)
|
postFlushCbs.push(cb)
|
||||||
}
|
}
|
||||||
queueFlush(onError)
|
|
||||||
}
|
|
||||||
|
|
||||||
function queueFlush(onError?: ErrorHandler) {
|
|
||||||
if (!isFlushing) {
|
if (!isFlushing) {
|
||||||
const p = nextTick(flushJobs)
|
nextTick(flushJobs)
|
||||||
if (onError) p.catch(onError)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +69,11 @@ function flushJobs(seenJobs?: JobCountMap) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
job()
|
try {
|
||||||
|
job()
|
||||||
|
} catch (err) {
|
||||||
|
handleError(err, null, ErrorTypes.SCHEDULER)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
flushPostFlushCbs()
|
flushPostFlushCbs()
|
||||||
isFlushing = false
|
isFlushing = false
|
||||||
|
@ -3,7 +3,7 @@ import {
|
|||||||
ComponentInstance,
|
ComponentInstance,
|
||||||
callWithAsyncErrorHandling
|
callWithAsyncErrorHandling
|
||||||
} from '@vue/runtime-core'
|
} from '@vue/runtime-core'
|
||||||
import { UserExecutionContexts } from 'packages/runtime-core/src/errorHandling'
|
import { ErrorTypes } from 'packages/runtime-core/src/errorHandling'
|
||||||
|
|
||||||
interface Invoker extends Function {
|
interface Invoker extends Function {
|
||||||
value: EventValue
|
value: EventValue
|
||||||
@ -77,7 +77,7 @@ function createInvoker(value: any, instance: ComponentInstance | null) {
|
|||||||
callWithAsyncErrorHandling(
|
callWithAsyncErrorHandling(
|
||||||
value[i],
|
value[i],
|
||||||
instance,
|
instance,
|
||||||
UserExecutionContexts.NATIVE_EVENT_HANDLER,
|
ErrorTypes.NATIVE_EVENT_HANDLER,
|
||||||
args
|
args
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -85,7 +85,7 @@ function createInvoker(value: any, instance: ComponentInstance | null) {
|
|||||||
callWithAsyncErrorHandling(
|
callWithAsyncErrorHandling(
|
||||||
value,
|
value,
|
||||||
instance,
|
instance,
|
||||||
UserExecutionContexts.NATIVE_EVENT_HANDLER,
|
ErrorTypes.NATIVE_EVENT_HANDLER,
|
||||||
args
|
args
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user