我试图在TypeScript中做到这一点,但我认为这个问题比TypeScript更广泛,也适用于JavaScript,因此这两个标签都适用。
我正在编写一些通用算法函数(例如Nelder-Mead优化器等),这些函数将用户提供的函数作为参数。我希望该算法同时支持同步和异步用户功能。有办法吗?
例如,让我们采用以下简化算法:
function findZero(f: (x: number) => number): number {
for(let i = 0; i < 1000; i++) {
const fi = f(i)
if(fi == 0) return i
}
return -1
}
console.log(findZero(x => 5-x)) // outputs: 5
console.log(findZero(x => new Promise<number>(resolve => resolve(5-x)))) // outputs: -1, since a Promise != 0
现在很明显,我可以很容易地将其转换为异步版本:
async function asyncFindZero(f: (x: number) => Promise<number>): Promise<number> {
for(let i = 0; i < 1000; i++) {
const fi = await f(i)
if(fi == 0) return i
}
return -1
}
console.log(asyncFindZero(x => 5-x)) // outputs: Promise { <pending> }
console.log(await asyncFindZero(x => 5-x)) // outputs: 5
console.log(await asyncFindZero(x => new Promise<number>(resolve => resolve(5-x)))) // outputs: 5
但我想避免有两个几乎相同的功能,只有几个
await
s和
Promise
s的差异。
是否有方法转换或(重写)算法函数,使其既可以:
-
将同步用户函数作为参数,并返回结果
-
或者接受一个异步用户函数并返回一个Promise?
有签名的东西,比如:
function findZero<MaybeAsync extends number|Promise<number>>(
f: (x: number) => MaybeAsync,
): MaybeAsync {
// How to write this?
}
// Such that these both work:
console.log(findZero(x => 5-x)) // outputs: 5
console.log(await findZero(x => new Promise<number>(resolve => resolve(5-x)))) // outputs: 5
或者,有两个单独的函数也可以,因为我静态地知道我需要哪个变体。类似于:
function masterFindZero(/*...*/) {/*...*/}
const syncFindZero = convertToSync(masterFindZero)
const asyncFindZero = convertToAsync(masterFindZero)
实际的算法显然更复杂,但我希望一旦我学会了缺失的概念,我就可以自己概括它。