- 一个简化版的实现如下(支持迭代返回计时器 id):
const every = async function* (interval) {
let resolve = null
const id = setInterval(() => {
resolve(id)
}, interval)
while (true) {
yield await new Promise(r => resolve = r)
}
}
;(async () => {
for await (const id of every(1000)) {
if (tick > 9) {
clearInterval(id)
}
}
})()
- 可以在此基础上考虑支持
break
作为clearInterval
的语义,于此同时返回值可以用来作为循环次数:
const every = async function* (interval) {
let resolve = null
let count = 0
const id = setInterval(() => {
if (resolve) {
resolve(count++)
resolve = null
} else {
clearInterval(id)
}
}, interval)
while (!resolve) {
yield await new Promise(r => resolve = r)
}
}
;(async () => {
for await (const tick of every(1000)) {
if (tick > 9) break
}
})()
- 如果不使用
setInterval
,代码可以更简便。下面的sleep
是一个很常用的异步 sleep 实现,缺点是如果循环耗时较多,实际时间不够精准
const sleep = timeout => new Promise(resolve => setTimeout(resolve, timeout))
const every = async function* (interval) {
while (true) yield await sleep(interval)
}
;(async () => {
for await (const _ of every(1000)) {
if (Math.random() > 0.5) break
}
})()
注意,目前的语法(ES2018)不支持箭头函数的异步生成器。
- 上面的形式也可以用其他的执行函数
const after = async function* (executor) {
while (true) {
yield await new Promise(executor)
}
}
;(async () => {
for await (const _ of after(r => r()))) {
if (Math.random() > 0.9999) break
}
for await (const _ of after(requestAnimationFrame)) {
if (Math.random() > 0.9999) break
}
for await (const _ of after(requestIdleCallback)) {
if (Math.random() > 0.9999) break
}
})()