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 { 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
} }
} }

View File

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

View File

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

View File

@ -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) {
} }
} }
} }
try {
job() job()
} catch (err) {
handleError(err, null, ErrorTypes.SCHEDULER)
}
} }
flushPostFlushCbs() flushPostFlushCbs()
isFlushing = false isFlushing = false

View File

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