直到Async/Await
,当我们需要每隔X秒做一次经常性的N次时,我们可以在包裹settimeout
的for循环之间做出选择,或者在setinterval
周围使用包装来计算它执行的次数并清除它。
但是使用异步等待是可以的:
async function newFashion() {
for (let i = 0; i < 10; i++) {
console.log(i);
await sleep(1000);
}
console.log("Finish");
}
newFashion(); // 0 1 2 3 4 5 6 7 8 9 Finish
function oldFashion() {
for (let i = 0; i < 10; i++) {
setTimeout(() => {
console.log(i);
}, i * 1000);
}
console.log("Finish");
}
oldFashion(); // Finish 0 1 2 3 4 5 6 7 8 9
function sleep(time) {
return new Promise((resolve) => {
setTimeout(resolve, time);
});
}
我知道两者的行为不一样。 oldFashion()
实际上并没有在循环中“暂停”,但实际上在迭代时调用10 setTimeout()
,所以基本上他用10 setTimeout()
填充事件循环。
然而,newFashion()
表现得应该如此,他在等待时“停止”迭代,但我不确定那是怎么回事。
那么更好的是,用N setTimeout()
填充事件循环,或者一个接一个地等待?在newFashion()
实际发生了什么?
编辑:更具体一点,每种方法在性能方面的收益是多少?当N很小?当N很大?
这是编写“老式”版本的更好方法。它保留了"Finish"
日志最后的理想行为,并且它不依赖于将来在不同的时间间隔立即设置一系列定时器。
整个事情是自成一体的,清晰的。
function betterOldFashion(i, n) {
if (i < n) {
console.log(i);
setTimeout(betterOldFashion, 1000, i+1, n);
} else {
console.log("Finish");
}
}
betterOldFashion(0, 10);
您建议的“老式”方式看起来不太可读,因为您没有将行为的一部分抽象为单独的函数,就像您使用“新”方式一样,出于某种原因,您将其置于“老式”方式之下。
为了给出两者相同的好处,这里重写了你的方式,尽管首先不使用命令式循环结构更好,IMO。
function oldFashion() {
for (let i = 0; i < 10; i++) {
schedule(i, i);
}
schedule("Finish", 10);
}
oldFashion();
function schedule(msg, i) {
setTimeout(() => {
console.log(msg);
}, i * 1000);
}
WRT性能,通常不太关心异步代码,很难衡量。顶部的解决方案当然具有最低的复杂性,如果这是效率的任何指标,并且您的旧方法版本必须立即安排许多事情,创建更多的后台工作。
而newFashion()实际上发生了什么?
await
表达式导致newFashion
函数的调用暂停,直到sleep
函数的结果(即Promise)将被解析或拒绝。在解析或拒绝之后,它将继续执行代码的其余部分。
那么更好的是,用N setTimeout()填充事件循环,或者一个接一个地等待?
如您所见,async/await
部分更具可读性。如果环境支持ES8
,您可以自由使用它。 async/await
部分也是熟悉的程序员,他们使用C#等语言进入Javascript,它也使用async/await
进行异步编程。对他们来说,使用async/await
代替你拥有的for loop
例子更方便。