expect(serializeInner(root)).toBe('resolved')
})
+ // #2129
+ test('error with error component, without global handler', async () => {
+ let resolve: (comp: Component) => void
+ let reject: (e: Error) => void
+ const Foo = defineAsyncComponent({
+ loader: () =>
+ new Promise((_resolve, _reject) => {
+ resolve = _resolve as any
+ reject = _reject
+ }),
+ errorComponent: (props: { error: Error }) => props.error.message
+ })
+
+ const toggle = ref(true)
+ const root = nodeOps.createElement('div')
+ const app = createApp({
+ render: () => (toggle.value ? h(Foo) : null)
+ })
+
+ app.mount(root)
+ expect(serializeInner(root)).toBe('<!---->')
+
+ const err = new Error('errored out')
+ reject!(err)
+ await timeout()
+ expect(serializeInner(root)).toBe('errored out')
+ expect(
+ 'Unhandled error during execution of async component loader'
+ ).toHaveBeenWarned()
+
+ toggle.value = false
+ await nextTick()
+ expect(serializeInner(root)).toBe('<!---->')
+
+ // errored out on previous load, toggle and mock success this time
+ toggle.value = true
+ await nextTick()
+ expect(serializeInner(root)).toBe('<!---->')
+
+ // should render this time
+ resolve!(() => 'resolved')
+ await timeout()
+ expect(serializeInner(root)).toBe('resolved')
+ })
+
test('error with error + loading components', async () => {
let resolve: (comp: Component) => void
let reject: (e: Error) => void
const onError = (err: Error) => {
pendingRequest = null
- handleError(err, instance, ErrorCodes.ASYNC_COMPONENT_LOADER)
+ handleError(
+ err,
+ instance,
+ ErrorCodes.ASYNC_COMPONENT_LOADER,
+ !errorComponent /* do not throw in dev if user provided error component */
+ )
}
// suspense-controlled or SSR.
if (timeout != null) {
setTimeout(() => {
- if (!loaded.value) {
+ if (!loaded.value && !error.value) {
const err = new Error(
`Async component timed out after ${timeout}ms.`
)
export function handleError(
err: unknown,
instance: ComponentInternalInstance | null,
- type: ErrorTypes
+ type: ErrorTypes,
+ throwInDev = true
) {
const contextVNode = instance ? instance.vnode : null
if (instance) {
return
}
}
- logError(err, type, contextVNode)
+ logError(err, type, contextVNode, throwInDev)
}
-function logError(err: unknown, type: ErrorTypes, contextVNode: VNode | null) {
+function logError(
+ err: unknown,
+ type: ErrorTypes,
+ contextVNode: VNode | null,
+ throwInDev = true
+) {
if (__DEV__) {
const info = ErrorTypeStrings[type]
if (contextVNode) {
if (contextVNode) {
popWarningContext()
}
- // crash in dev so it's more noticeable
- throw err
+ // crash in dev by default so it's more noticeable
+ if (throwInDev) {
+ throw err
+ } else {
+ console.error(err)
+ }
} else {
// recover in prod to reduce the impact on end-user
console.error(err)