如何将异步/等待代码(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的赋值)
- 其中
- 这会导致一些深层嵌套,但如果我遵循这些步骤,我发现自己犯错误的可能性较小
- 完成并验证后,我就可以进去清理事情
// 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/