typescript - Typescript 中的链接 Promise

标签 typescript promise async-await

如何将异步/等待代码(Typescript + es6 target)转换为使用链式Promise.then()

例如:

function mockDelay<T>(getValue:()=>T): Promise<T> {
    return new Promise<T>(resolve=>setTimeout(()=>resolve(getValue()), 10));
}
// Assume blackbox implementation
class Service {
    constructor(private i=1, private callCount=0){}
    opA() : Promise<number> { 
        this.callCount++; 
        return mockDelay(()=>this.i+=1);
    }
    opB(value:number) : Promise<number> {
        this.callCount++;    
        return mockDelay(()=>this.i+=value);
    }

    opC(): Promise<number> {
        return mockDelay(()=>this.i+=2);
    }

    isA(): Promise<boolean> { return mockDelay(()=>this.callCount%2===0); }
    isC(): Promise<boolean> { return mockDelay(() =>true); }
}

// Convert this async/await code to using chained Promises
async function asyncVersion(): Promise<string[]>{
    let expected:string[] = [];
    let lib = new Service();
    let sum = 20;
    let lastValue = 0;
    while (sum > 0) {
        expected.push(`${sum} left`);
        if (await lib.isA())
        {
            expected.push("Do A()");
            lastValue = await lib.opA();
            sum -= lastValue;
        }
        else
        {
            expected.push("Do B()");
            lastValue = await lib.opB(lastValue);
            sum -= lastValue*3;
            if (await lib.isC()) {
                expected.push("Do C()");
                sum += await lib.opC();
            }
        }
    }
    expected.push("All completed!");
    return expected;
};

function chainPromiseVersion(): Promise<string[]>{
    // How to convert the asyncVersion() to using chained promises?
    return Promise.resolve([]);
} 

// Compare results
// Currently running asyncVersion() twice to ensure call results are consistent/no side effects
// Replace asyncVersion() with chainPromiseVersion() 
Promise.all([asyncVersion(), asyncVersion() /*chainPromiseVersion()*/])
    .then(result =>{
        let expected = result[0];
        let actual = result[1];
        if (expected.length !== actual.length) 
            throw new Error(`Length: expected ${expected.length} but was ${actual.length}`);
        for(let i=0; i<expected.length; i++) {
            if (expected[i] !== actual[i]){
                throw new Error(`Expected ${expected[i]} but was ${actual[i]}`);
            }
        }
    })
    .then(()=>console.log("Test completed"))
    .catch(e => console.log("Error: "  + e));

我知道我可以使用 Babel ( Github example ) 将 es6 代码转换为 es5。

这个问题是关于手动重写异步/等待代码以使用链式 promise 。

我可以转换如下简单示例。

// Async/Await
(async function(){
    for (let i=0; i<5; i++){
        let result = await mockDelay(()=>"Done " + i);
        console.log(result);
    }
    console.log("All done");
})();

// Chained Promises
(function(){
    let chain = Promise.resolve(null);
    for (let i=0; i<5; i++){
        chain = chain
            .then(()=>mockDelay(()=>"Done " + i))
            .then(result => console.log(result));
    }
    chain.then(()=>console.log("All done"));
})();

但不知道如何转换上面的示例,其中:

  • 循环条件受 Promise 结果影响
  • 执行必须是一个接着一个(没有 Promise.all())

最佳答案

感谢 Bergi 的回答,我想我已经弄清楚如何从 async/await 逐步转换为链式 promise

我创建了一个辅助函数 promiseWhile 来帮助自己:

// Potential issue: recursion could lead to stackoverflow
function promiseWhile(condition:()=>boolean, loopBody: ()=>Promise<any>): Promise<any> {
    if (condition()) {
        return loopBody().then(()=>promiseWhile(condition, loopBody));
    } else {
        // Loop terminated
        return null;
    }
}

我使用的步骤:

  • 每当 await op() 被命中时,转换为 return op().then(()=>{...})
    • 其中{...}await之后的代码(包括await的赋值)
  • 这会导致一些深层嵌套,但如果我遵循这些步骤,我发现自己犯错误的可能性较小
  • 完成并验证后,我就可以进去清理事情

Conversion

// Converted
function chainPromiseVersion(): Promise<string[]>{
    let expected:string[] = [];
    let lib = new Service();
    let sum = 20;
    let lastValue = 0;
    return promiseWhile(
        // Loop condition
        ()=>sum>0,

        // Loop body 
        ()=> {
            expected.push(`${sum} left`);

            return Promise.resolve(null)
            .then(()=>lib.isA())
            .then(isA => {
                if (isA) {
                    expected.push("Do A()");
                    return lib.opA()
                        .then(v =>{
                            lastValue = v;
                            sum -= lastValue;
                        });
                }
                else {
                    expected.push("Do B()");
                    return lib.opB(lastValue)
                            .then(v=>{
                                lastValue = v;
                                sum -= lastValue*3;
                                return lib.isC().then(isC => {
                                    if (isC) {
                                        expected.push("Do C()");
                                        return lib.opC().then(v => {
                                            sum += v;
                                        });
                                    }
                                });
                            });
                }
            }) // if (lib.isA()) 
        }) // End loop body
        .then(()=>expected.push("All completed!"))
        .then(()=>expected);
}

关于typescript - Typescript 中的链接 Promise,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38764277/

相关文章:

javascript - TypeORM - 获取用户时永远不会从数据库返回密码

javascript - Angular - 处理 promise 链第一个函数中 $q 中的异常

JavaScript:代码移动得太快,我应该写一个 Promise 吗?

JavaScript - 同步等待异步操作(休眠)

Angular 2 : Updating and sharing data between components

javascript - ESLint:防止对非标准文件扩展名进行 lint

angular - 存在性能问题的选项卡内容

javascript - 将 for..in 循环与 Promise 和异步函数结合使用

c# - Task.Yield、Task.Run 和 ConfigureAwait(false) 之间有什么区别?

c# - 我应该使用 c# mongodb 驱动程序的同步方法还是异步方法?