From a5b2382da6785e1c7c5aff394d40abbee6d4b26e Mon Sep 17 00:00:00 2001 From: Taku Amano Date: Thu, 15 Aug 2024 20:27:52 +0900 Subject: [PATCH] fix(jsx): await all promises at once to avoid race condition (#3274) --- src/jsx/index.test.tsx | 33 +++++++++++++++++++++++++++++++++ src/utils/html.ts | 7 ++++--- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/jsx/index.test.tsx b/src/jsx/index.test.tsx index 1fb7063e..5381e642 100644 --- a/src/jsx/index.test.tsx +++ b/src/jsx/index.test.tsx @@ -112,6 +112,39 @@ describe('JSX middleware', () => { expect(res.headers.get('Content-Type')).toBe('text/html; charset=UTF-8') expect(await res.text()).toBe('

Hello from async component

') }) + + it('Should handle async component error', async () => { + const componentError = new Error('Error from async error component') + + const AsyncComponent = async () => { + await new Promise((resolve) => setTimeout(resolve, 10)) + return

Hello from async component

+ } + const AsyncErrorComponent = async () => { + await new Promise((resolve) => setTimeout(resolve, 0)) + throw componentError + } + + let raisedError: any + app.onError((e, c) => { + raisedError = e + return c.html('

Error from onError

', 500) + }) + app.get('/', (c) => { + return c.html( + <> + + + + ) + }) + + const res = await app.request('http://localhost/') + expect(res.status).toBe(500) + expect(res.headers.get('Content-Type')).toBe('text/html; charset=UTF-8') + expect(await res.text()).toBe('

Error from onError

') + expect(raisedError).toBe(componentError) + }) }) describe('render to string', () => { diff --git a/src/utils/html.ts b/src/utils/html.ts index 435828a4..d3557263 100644 --- a/src/utils/html.ts +++ b/src/utils/html.ts @@ -56,14 +56,15 @@ export const stringBufferToString = async ( ): Promise => { let str = '' callbacks ||= [] - for (let i = buffer.length - 1; ; i--) { - str += buffer[i] + const resolvedBuffer = await Promise.all(buffer) + for (let i = resolvedBuffer.length - 1; ; i--) { + str += resolvedBuffer[i] i-- if (i < 0) { break } - let r = await buffer[i] + let r = resolvedBuffer[i] if (typeof r === 'object') { callbacks.push(...((r as HtmlEscapedString).callbacks || [])) }