fix: stack overflow on large number of templates, #513

This commit is contained in:
Harttle
2022-07-08 01:46:51 +08:00
parent 2f87708989
commit 3dc4290b56
16 changed files with 172 additions and 180 deletions
+38 -56
View File
@@ -7,25 +7,6 @@ export interface Thenable<T> {
catch (reject: resolver): Thenable<T>;
}
function createResolvedThenable<T> (value: T): Thenable<T> {
const ret = {
then: (resolve: resolver) => resolve(value),
catch: () => ret
}
return ret
}
function createRejectedThenable<T> (err: Error): Thenable<T> {
const ret = {
then: (resolve: resolver, reject?: resolver) => {
if (reject) return reject(err)
return ret
},
catch: (reject: resolver) => reject(err)
}
return ret
}
function isThenable<T> (val: any): val is Thenable<T> {
return val && isFunction(val.then)
}
@@ -34,48 +15,49 @@ function isAsyncIterator (val: any): val is IterableIterator<any> {
return val && isFunction(val.next) && isFunction(val.throw) && isFunction(val.return)
}
// convert an async iterator to a thenable (Promise compatible)
export function toThenable<T> (val: IteratorResult<unknown, T> | Thenable<T> | any): Thenable<T> {
if (isThenable(val)) return val
if (isAsyncIterator(val)) return reduce()
return createResolvedThenable(val)
function reduce<T> (prev?: T): Thenable<T> {
let state
// convert an async iterator to a Promise
export async function toPromise<T> (val: Generator<unknown, T, unknown> | Thenable<T> | T): Promise<T> {
if (!isAsyncIterator(val)) return val
let value: unknown
let done = false
let next = 'next'
do {
const state = val[next](value)
done = state.done
value = state.value
next = 'next'
try {
state = val.next(prev)
if (isAsyncIterator(value)) value = toPromise(value)
if (isThenable(value)) value = await value
} catch (err) {
return createRejectedThenable(err as Error)
next = 'throw'
value = err
}
} while (!done)
return value as T
}
if (state.done) return createResolvedThenable(state.value)
return toThenable(state.value!).then(reduce, err => {
let state
// convert an async iterator to a value in a synchronous maner
export function toValue<T> (val: Generator<unknown, T, unknown> | T): T {
if (!isAsyncIterator(val)) return val
let value: any
let done = false
let next = 'next'
do {
const state = val[next](value)
done = state.done
value = state.value
next = 'next'
if (isAsyncIterator(value)) {
try {
state = val.throw!(err)
} catch (e) {
return createRejectedThenable(e as Error)
value = toValue(value)
} catch (err) {
next = 'throw'
value = err
}
if (state.done) return createResolvedThenable(state.value)
return reduce(state.value)
})
}
}
} while (!done)
return value
}
export function toPromise<T> (val: Generator<unknown, T, unknown> | Thenable<T> | T): Promise<T> {
return Promise.resolve(toThenable(val))
}
// get the value of async iterator in synchronous manner
export function toValue<T> (val: Generator<unknown, T, unknown> | Thenable<T> | T): T {
let ret: T
toThenable(val)
.then((x: any) => {
ret = x
return createResolvedThenable(ret)
})
.catch((err: Error) => {
throw err
})
return ret!
}
export const toThenable = toPromise