Skip to content

Commit

Permalink
Fix idleTimeout error being thrown in route loader (#22775)
Browse files Browse the repository at this point in the history
In the current implementation, `idleTimeout` will always be thrown even if it didn't time out and `Promise.race` was resolved. This causes the error `Error: Route did not complete loading` on every route transition and Chrome Devtools will pause code execution if you have "Pause on exceptions" enabled.

This PR adds `resolvePromiseWithTimeout` which does the same thing as `Promise.race` and `idleTimeout`, but it cancels the rejection when it resolves successfully, in which case the error won't be thrown.

Fixes #21543.
  • Loading branch information
shuding committed Mar 5, 2021
1 parent 33255b7 commit afc8ce4
Showing 1 changed file with 35 additions and 18 deletions.
53 changes: 35 additions & 18 deletions packages/next/client/route-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,29 @@ function appendScript(
})
}

function idleTimeout<T>(ms: number, err: Error): Promise<T> {
return new Promise((_resolve, reject) =>
requestIdleCallback(() => setTimeout(() => reject(err), ms))
)
// Resolve a promise that times out after given amount of milliseconds.
function resolvePromiseWithTimeout<T>(
p: Promise<T>,
ms: number,
err: Error
): Promise<T> {
return new Promise((resolve, reject) => {
let cancelled = false

p.then((r) => {
// Resolved, cancel the timeout
cancelled = true
resolve(r)
}).catch(reject)

requestIdleCallback(() =>
setTimeout(() => {
if (!cancelled) {
reject(err)
}
}, ms)
)
})
}

// TODO: stop exporting or cache the failure
Expand All @@ -176,13 +195,12 @@ export function getClientBuildManifest(): Promise<ClientBuildManifest> {
cb && cb()
}
})
return Promise.race([

return resolvePromiseWithTimeout<ClientBuildManifest>(
onBuildManifest,
idleTimeout<ClientBuildManifest>(
MS_MAX_IDLE_DELAY,
markAssetError(new Error('Failed to load client build manifest'))
),
])
MS_MAX_IDLE_DELAY,
markAssetError(new Error('Failed to load client build manifest'))
)
}

interface RouteFiles {
Expand Down Expand Up @@ -298,15 +316,14 @@ function createRouteLoader(assetPrefix: string): RouteLoader {
Promise.all(css.map(fetchStyleSheet)),
] as const)

const entrypoint: RouteEntrypoint = await Promise.race([
const entrypoint: RouteEntrypoint = await resolvePromiseWithTimeout(
this.whenEntrypoint(route),
idleTimeout<RouteLoaderEntry>(
MS_MAX_IDLE_DELAY,
markAssetError(
new Error(`Route did not complete loading: ${route}`)
)
),
])
MS_MAX_IDLE_DELAY,
markAssetError(
new Error(`Route did not complete loading: ${route}`)
)
)

const res: RouteLoaderEntry = Object.assign<
{ styles: RouteStyleSheet[] },
RouteEntrypoint
Expand Down

0 comments on commit afc8ce4

Please sign in to comment.