代码之家  ›  专栏  ›  技术社区  ›  Niobos

如何/是否有方法使通用算法函数同时与同步和异步函数一起工作?

  •  0
  • Niobos  · 技术社区  · 2 周前

    我试图在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)
    

    实际的算法显然更复杂,但我希望一旦我学会了缺失的概念,我就可以自己概括它。

    1 回复  |  直到 2 周前
        1
  •  1
  •   Bergi    2 周前

    您可以将主函数编写为生成器函数,然后同步或异步运行它:

    function* findZero(): Generator<number, number, number> {
        for (let i = 0; i < 1000; i++) {
            const fi = yield i;
            if (fi == 0) return i;
        }
        return -1;
    }
    function findZeroSync(f: (x: number) => number): number {
        const gen = findZero();
        let step = gen.next();
        while (!step.done) {
            step = gen.next(f(step.value));
        }
        return step.value;
    }
    async function findZeroAsync(f: (x: number) => Promise<number>): Promise<number> {
        const gen = findZero();
        let step = gen.next();
        while (!step.done) {
            step = gen.next(await f(step.value));
        }
        return step.value;
    }