Skip to content

Commit

Permalink
fix(runtime-core/async-component): fix error component when there are…
Browse files Browse the repository at this point in the history
… no error handlers

fix #2129
  • Loading branch information
yyx990803 committed Sep 16, 2020
1 parent bad0ecb commit c7b4a37
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 7 deletions.
45 changes: 45 additions & 0 deletions packages/runtime-core/__tests__/apiAsyncComponent.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,51 @@ describe('api: defineAsyncComponent', () => {
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
Expand Down
9 changes: 7 additions & 2 deletions packages/runtime-core/src/apiAsyncComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,12 @@ export function defineAsyncComponent<

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.
Expand Down Expand Up @@ -152,7 +157,7 @@ export function defineAsyncComponent<

if (timeout != null) {
setTimeout(() => {
if (!loaded.value) {
if (!loaded.value && !error.value) {
const err = new Error(
`Async component timed out after ${timeout}ms.`
)
Expand Down
20 changes: 15 additions & 5 deletions packages/runtime-core/src/errorHandling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ export function callWithAsyncErrorHandling(
export function handleError(
err: unknown,
instance: ComponentInternalInstance | null,
type: ErrorTypes
type: ErrorTypes,
throwInDev = true
) {
const contextVNode = instance ? instance.vnode : null
if (instance) {
Expand Down Expand Up @@ -131,10 +132,15 @@ export function handleError(
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) {
Expand All @@ -144,8 +150,12 @@ function logError(err: unknown, type: ErrorTypes, contextVNode: VNode | null) {
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)
Expand Down

0 comments on commit c7b4a37

Please sign in to comment.