红绿灯

设计一个程序,模拟一个红绿灯

const serial = ['Red', 'Yellow', 'Green']

function delay(duration = 1000) {
  return new Promise(resolve => setTimeout(resolve, duration))
}

class Signal {
  get next() {
    return serial[(serial.indexOf(this.sig) + 1) % serial.length]
  }

  get remain() {
    let diff = this.end - Date.now()
    if (diff < 0) {
      diff = 0
    }
    return diff / 1000
  }

  constructor(options) {
    this.sig = options.init
    this.times = options.times
    this.eventMap = new Map()
    this.eventMap.set('change', new Set())
    this.eventMap.set('tick', new Set())
    this.setTime()
    this.exchange()
  }

  on(event, handler) {
    this.eventMap.get(event).add(handler)
  }

  off(event, handler) {
    this.eventMap.get(event).delete(handler)
  }

  emit(event) {
    this.eventMap.get(event).forEach(h => {
      h.call(this, this)
    })
  }

  async exchange() {
    // 为什么要await 1,因为第一次emit('tick')的时候`on`还没有开始监听,所以第一次不会打印
    // 所以把后面的任务放在一个微队列中来解决这个问题
    await 1;
    if (this.remain > 0) {
      // 不需要切换
      this.emit('tick')
      await delay(1000)
    } else {
      this.sig = this.next
      this.setTime()
      this.emit('change')
    }
    this.exchange()
  }

  setTime() {
    this.start = Date.now()
    const time = this.times[serial.indexOf(this.sig)]
    this.end = this.start + time * 1000
  }
}
const s = new Signal({
  init: 'Red',
  times: [10, 3, 5]
})
s.on('change', (e) => {
  console.log(e.sig, e.next, e.remain)
})
const handler = (e) => {
  console.log(e.sig, e.next, e.remain)
}
s.on('tick', handler)
Last Updated: