如何将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。
这个问题是关于手动重写async / await代码以使用链式承诺。
我可以转换如下的简单示例。
// 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.all()
)答案 0 :(得分:3)
感谢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
之后的代码(包括等待分配)// 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);
}
答案 1 :(得分:1)
await
成为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);
幸运的是,你的循环中没有break
/ continue
/ return
形式,因为这会让它变得更加复杂。通常,将所有语句转换为连续传递样式,然后您可以在必要时将它们推迟。