typescript 在打字稿中链接承诺

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/38764277/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 03:43:20  来源:igfitidea点击:

Chaining Promises in Typescript

typescriptpromiseasync-await

提问by jayars

How do I convert async/await code (Typescript + es6 target)to using chained Promise.then()?

如何将 async/await 代码转换(Typescript + es6 target)为使用 chained Promise.then()

For example:

例如:

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));

I know I can transpile es6 code to es5 using Babel (Github example).

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

This question is about manually rewriting async/await code to using chained promises.

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

I can convert simple examples like the following.

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

// 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"));
})();

But have no idea how to convert the example above, where:

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

  • loop condition affected by promise result
  • execution must be one-after-the-other (no Promise.all())
  • 受承诺结果影响的循环条件
  • 执行必须是一个接一个(否Promise.all()

采纳答案by Bergi

awaits become thencalls - often nested ones for scoping and control flow to be effective - and loops become recursion. In your case:

awaits 变成then调用——通常是嵌套的,用于范围和控制流有效——循环变成递归。在你的情况下:

(function loop(lib, sum, lastValue){
    if (sum > 0) {
        console.log(`${sum} left`);
        return lib.isA().then(res => {
            if (res) {
                console.log("Do A()");
                return lib.opA().then(lastValue => {
                    sum -= lastValue;
                    return loop(lib, sum, lastValue);
                });
            } else {
                console.log("Do B()");
                return lib.opB(lastValue).then(lastValue => {
                    sum -= lastValue*3;
                    return lib.isC().then(res => {
                        if (res) {
                            console.log("Do C()");
                            return lib.opC().then(res => {
                                sum += res;
                                return loop(lib, sum, lastValue);
                            });
                        }
                        return loop(lib, sum, lastValue);
                    });
                });
            }
        });
    } else {
        console.log("All completed!");
        return Promise.resolve()
    }
})(new Service(), 20, 0);

Luckily you had no form of break/continue/returninside your loop, as that would've made it even more complicated. In general, convert all statements to continuation passing style, then you can defer them where necessary.

幸运的是,您的循环中没有break/ continue/形式return,因为这会使它变得更加复杂。通常,将所有语句转换为连续传递样式,然后您可以在必要时推迟它们。

回答by jayars

Thanks to Bergi's answer, I think I've figured out how to do a step-by-step conversion from async/await to chained promise.

感谢 Bergi 的回答,我想我已经找到了从 async/await 到 chained 的逐步转换的方法promise

I've created a helper function promiseWhileto help myself:

我创建了一个辅助函数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;
    }
}

The steps I've used:

我使用的步骤:

  • Whenever await op()is hit, convert to return op().then(()=>{...})
    • where {...}is the code after the await(including assignment from await)
  • This results in some deep nesting, but if I follow these steps, I find myself less likely to make a mistake
  • Once completed and verified, I can then go in and clean things up
  • 每当await op()被击中时,转换为return op().then(()=>{...})
    • {...}后面的代码在哪里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);
}