CyanSalt / notebook

3 stars 0 forks source link

使用 Async Iterator 实现异步定时器 #14

Open CyanSalt opened 6 years ago

CyanSalt commented 6 years ago

path: async-timer


  1. 一个简化版的实现如下(支持迭代返回计时器 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)
    }
  }
})()
  1. 可以在此基础上考虑支持 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
  }
})()
  1. 如果不使用 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)不支持箭头函数的异步生成器。

  1. 上面的形式也可以用其他的执行函数
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
  }
})()