将无限循环中的等待转换为原始 promise.then
Converting await in infinite for loop into raw promise.then
据我所知,async/await
只是 promise.then
的语法糖。考虑这个代码片段:
function sleep(n){
return new Promise(res => setTimeout(res, n));
}
function* range(n){
var i = 0;
while(i < n) yield i++;
}
async function doStuff(){
for(let n of range(10)){
console.log(n); // print the number
await sleep(1000); // wait for 1 second
}
}
async/await
使代码非常线性、高效且易于理解。要记住的一件事是 range
不必有一个实际的结束就可以工作。
现在的问题是如何使用 ES7 之前的 promise.then
重写它。这是同一循环的可能实现:
function doStuff(){
return Array.from(range(10)).reduce((acc, ele) => {
return acc
.then(() => console.log(ele))
.then(() => sleep(1000))
}, Promise.resolve());
}
忽略代码不是很优雅的事实,使用Array.from(range(10))
- 创建一个不需要的额外数组,并且
- 假设
range(10)
将在未来的某个时刻结束。
看起来不是很好的转换。
我们也可以通过将 yield
用作 await
来完全重新发明轮子,但这会使语法不符合 ES5。这里的目标是:
- 使用符合 ES5 的语法重写
- 使用承诺返回
sleep
函数
- 动态链接
sleep
承诺,同时允许迭代器没有结束
doStuff
可以链接:
doStuff().finally(cleanUp); // clean up if something failed
(可选)代码不应过于复杂
有什么想法吗?
我认为以下内容可能会起作用,您的示例没有说明如何处理解析值以及它与迭代器值的关系,因此我更改了睡眠的调用方式。
一些 promise polyfils 可能 运行 出栈 space 高 promise 链所以你应该检查你的 polyfil(如果它的实现 returns 并继续使用 setTimeout 堆栈应该清除但有些 polyfils 可能不会以这种方式实现它。
function sleep(n){
return new Promise(res => setTimeout(_=>res(n/100), n));
}
function* range(n){
var i = 0;
while(i < n) yield i++;
}
function doStuff(){
const processValue =
resolve => {
console.log("resolved with:",resolve);
// if(resolve===3){throw "nope";}
return sleep(resolve*100);
},
rec = (p,iter) => {
const result = iter.next();
if (result.done){
return p;
}
p = p.then(_=>processValue(result.value))
return p.then(
resolve=>{
return rec(p,iter)
}
);
},
iter = range(10),
firstResult = iter.next();
if(firstResult.done){
return processValue(firstResult.value);
}
return rec(processValue(firstResult.value),iter);
}
doStuff()
.then(
x=>console.log("done:",x)
,reject=>console.warn("fail:",reject)
);
我一直说,如果您需要异步设计模式,请先查看 async library. In this case, since you're using promises, take a look at the promisified async-q 库。翻译很直接:
var n = 0;
async.whilst(() => n < 10, () => {
n++;
console.log(n);
return sleep(1000);
})
据我所知,async/await
只是 promise.then
的语法糖。考虑这个代码片段:
function sleep(n){
return new Promise(res => setTimeout(res, n));
}
function* range(n){
var i = 0;
while(i < n) yield i++;
}
async function doStuff(){
for(let n of range(10)){
console.log(n); // print the number
await sleep(1000); // wait for 1 second
}
}
async/await
使代码非常线性、高效且易于理解。要记住的一件事是 range
不必有一个实际的结束就可以工作。
现在的问题是如何使用 ES7 之前的 promise.then
重写它。这是同一循环的可能实现:
function doStuff(){
return Array.from(range(10)).reduce((acc, ele) => {
return acc
.then(() => console.log(ele))
.then(() => sleep(1000))
}, Promise.resolve());
}
忽略代码不是很优雅的事实,使用Array.from(range(10))
- 创建一个不需要的额外数组,并且
- 假设
range(10)
将在未来的某个时刻结束。
看起来不是很好的转换。
我们也可以通过将 yield
用作 await
来完全重新发明轮子,但这会使语法不符合 ES5。这里的目标是:
- 使用符合 ES5 的语法重写
- 使用承诺返回
sleep
函数 - 动态链接
sleep
承诺,同时允许迭代器没有结束 doStuff
可以链接:doStuff().finally(cleanUp); // clean up if something failed
(可选)代码不应过于复杂
有什么想法吗?
我认为以下内容可能会起作用,您的示例没有说明如何处理解析值以及它与迭代器值的关系,因此我更改了睡眠的调用方式。
一些 promise polyfils 可能 运行 出栈 space 高 promise 链所以你应该检查你的 polyfil(如果它的实现 returns 并继续使用 setTimeout 堆栈应该清除但有些 polyfils 可能不会以这种方式实现它。
function sleep(n){
return new Promise(res => setTimeout(_=>res(n/100), n));
}
function* range(n){
var i = 0;
while(i < n) yield i++;
}
function doStuff(){
const processValue =
resolve => {
console.log("resolved with:",resolve);
// if(resolve===3){throw "nope";}
return sleep(resolve*100);
},
rec = (p,iter) => {
const result = iter.next();
if (result.done){
return p;
}
p = p.then(_=>processValue(result.value))
return p.then(
resolve=>{
return rec(p,iter)
}
);
},
iter = range(10),
firstResult = iter.next();
if(firstResult.done){
return processValue(firstResult.value);
}
return rec(processValue(firstResult.value),iter);
}
doStuff()
.then(
x=>console.log("done:",x)
,reject=>console.warn("fail:",reject)
);
我一直说,如果您需要异步设计模式,请先查看 async library. In this case, since you're using promises, take a look at the promisified async-q 库。翻译很直接:
var n = 0;
async.whilst(() => n < 10, () => {
n++;
console.log(n);
return sleep(1000);
})