0
0
mirror of https://github.com/honojs/hono.git synced 2024-12-01 11:51:01 +01:00

perf(compose): optimize await (#466)

Do not `await` if the handler is not promise.
This commit is contained in:
Yusuke Wada 2022-08-15 08:35:57 +09:00 committed by GitHub
parent 9edf43c63b
commit 578833ea85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 70 additions and 40 deletions

View File

@ -11,9 +11,10 @@ export const compose = <C>(
return (context: C, next?: Function) => {
let index = -1
return dispatch(0)
async function dispatch(i: number): Promise<C> {
if (i <= index) {
return Promise.reject(new Error('next() called multiple times'))
throw new Error('next() called multiple times')
}
let handler = middleware[i]
index = i
@ -23,27 +24,41 @@ export const compose = <C>(
if (context instanceof HonoContext && context.finalized === false && onNotFound) {
context.res = await onNotFound(context)
}
return Promise.resolve(context)
return context
}
return Promise.resolve(handler(context, () => dispatch(i + 1)))
.then((res: Response) => {
// If handler return Response like `return c.text('foo')`
if (res && context instanceof HonoContext) {
context.res = res
let res!: Response
let isError: boolean = false
try {
if (isPromise(handler)) {
res = await handler(context, () => dispatch(i + 1))
} else {
res = handler(context, () => dispatch(i + 1))
}
} catch (err) {
if (context instanceof HonoContext && onError) {
if (err instanceof Error) {
isError = true
res = onError(err, context)
}
return context
})
.catch((err) => {
if (context instanceof HonoContext && onError) {
if (err instanceof Error) {
context.res = onError(err, context)
}
return context
} else {
throw err
}
})
}
if (!res) {
throw err
}
}
if (res && context instanceof HonoContext && (!context.finalized || isError)) {
context.res = res
}
return context
}
}
}
function isPromise(p: Function) {
if (typeof p === 'function' && p.constructor.name === 'AsyncFunction') {
return true
}
return false
}

View File

@ -11,9 +11,10 @@ export const compose = <C>(
return (context: C, next?: Function) => {
let index = -1
return dispatch(0)
async function dispatch(i: number): Promise<C> {
if (i <= index) {
return Promise.reject(new Error('next() called multiple times'))
throw new Error('next() called multiple times')
}
let handler = middleware[i]
index = i
@ -23,27 +24,41 @@ export const compose = <C>(
if (context instanceof HonoContext && context.finalized === false && onNotFound) {
context.res = await onNotFound(context)
}
return Promise.resolve(context)
return context
}
return Promise.resolve(handler(context, () => dispatch(i + 1)))
.then((res: Response) => {
// If handler return Response like `return c.text('foo')`
if (res && context instanceof HonoContext) {
context.res = res
let res!: Response
let isError: boolean = false
try {
if (isPromise(handler)) {
res = await handler(context, () => dispatch(i + 1))
} else {
res = handler(context, () => dispatch(i + 1))
}
} catch (err) {
if (context instanceof HonoContext && onError) {
if (err instanceof Error) {
isError = true
res = onError(err, context)
}
return context
})
.catch((err) => {
if (context instanceof HonoContext && onError) {
if (err instanceof Error) {
context.res = onError(err, context)
}
return context
} else {
throw err
}
})
}
if (!res) {
throw err
}
}
if (res && context instanceof HonoContext && (!context.finalized || isError)) {
context.res = res
}
return context
}
}
}
function isPromise(p: Function) {
if (typeof p === 'function' && p.constructor.name === 'AsyncFunction') {
return true
}
return false
}

View File

@ -1033,7 +1033,7 @@ describe('Both two middleware returning response', () => {
const res = await app.request('http://localhost/')
expect(res).not.toBeNull()
expect(res.status).toBe(200)
expect(await res.text()).toBe('Foo')
expect(res.headers.get('content-type')).toBe('text/html; charset=UTF-8')
expect(await res.text()).toBe('Bar')
expect(res.headers.get('content-type')).toBe('text/plain; charset=UTF-8')
})
})