2. 交通灯问题

226 阅读2分钟

问题

模拟交通灯的的变化,支持监听距离跳转下一个红绿灯还有多久,什么时候跳转

问题的解答

思路

交通灯问题.png

核心点

  1. 交通灯总是 red->green->yellow->red
  2. 所以变化的常量数组可以得出;并且需要根据当前的颜色算出写一个要变化的颜色(所以当前颜色也就是初始化颜色,需要传入,有外部决定)
  3. 已知当前颜色,知道时间间隔就知道当前颜色结束的时间,这样就可以推算出每个时间点离当前颜色的结束点的时间差(也就是所需要知道,每个颜色停留的时间分别是多少,2中又已经知道了当前颜色,所以时间数组需要外部决定)
  4. 由2和3可以得到,需要的传参暂时定位初始化颜色,initcolor,时间间隔数组dutations
  5. 特殊情况下,可人工修改红绿灯颜色,以及每个颜色的停留时长,所以还需要提供相对应的方法
  6. 因为需要让外部函数知道当前还剩多少时间进入到下一个颜色,所以还是要用订阅发布给出对应的eventbus
  7. 核心函数echange,需要递归,检查什么时候该去切换颜色,并抛出对应的时间让外部知道当前的现状

代码

/**
 * 交通灯问题
 * @param {Object} initcolor 初始化颜色
 * @param {Object} durations 每个颜色停留时间
 * @methods changeColor 改变当前灯的颜色
 * @methods changeDurations 改变当前灯的停留时间
 * @methods pause 暂停
 * @methods turnon 继续
 */

const LIGHTS = ['red', 'green', 'yellow']

const sleep = (delay = 1000) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve()
    }, delay)
  })
}

class TrafficLightClass {
  constructor({ initcolor = 'red', durations = [10, 20, 3] } = {}) {
    this.currentColor = initcolor
    this.durations = durations
    this._isPauseing = false
    this._reamin_diff = 0
    this.eventBus = new Map()
    this.eventBus.set('change', new Set())
    this.eventBus.set('tick', new Set())
    this.setTime()
    this._exchange()
  }

  get nextColor() {
    return LIGHTS[(LIGHTS.indexOf(this.currentColor) + 1) % LIGHTS.length]
  }

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

  on(event, handle) {
    this.eventBus.get(event).add(handle)
  }

  off(event, handle) {
    this.eventBus.get(event).delete(handle)
  }

  emit(event) {
    this.eventBus.get(event).forEach(item => {
      item.call(this, this)
    })
  }

  async _exchange() {
    if (this._isPauseing) {
      this.emit('tick')
      await sleep()
      this._exchange()
      return
    }
    await 1
    if (this.remain > 0) {
      // note: 不需要切换
      this.emit('tick')
      await sleep()
    } else {
      // note: 需要切换交通灯了
      this.currentColor = this.nextColor
      this.emit('change')
      this.setTime()
    }
    this._exchange()
  }

  setTime() {
    this.start = Date.now()
    if (this._reamin_diff) {
      this.end = this.start + this._reamin_diff * 1000
      return
    }
    console.log('------->', this.currentColor)
    this.end = this.start + this.durations[LIGHTS.indexOf(this.currentColor)] * 1000
  }

  changeColor(color) {
    if (!LIGHTS.includes(color)) {
      return
    }
    this.currentColor = color
    this.setTime()
  }

  changeDurations(times) {
    this.durations = times
  }

  pause() {
    if (this._isPauseing) {
      return
    }
    this._reamin_diff = this.remain
    this._isPauseing = true
  }

  turnon() {
    if (!this._isPauseing) {
      return
    }
    this._isPauseing = false
    this.setTime()
    this._reamin_diff = 0
  }
}

const trafficLight = new TrafficLightClass({
  initcolor: 'red',
  durations: [10, 20, 3]
})

// note: 页面暂时当前灯以及还有多长时间切换
trafficLight.on('tick', e => {
  console.log('tick', e.currentColor, Math.round(e.remain))
})

// note: 改变停留时间
// trafficLight.changeDurations([2,3,4])

// note: 改变颜色(模拟按钮的点击,人工操作)
// setTimeout(() => {
//   trafficLight.changeColor('green')
// }, 3 * 1000)

// note: 暂停(模拟按钮的点击,人工操作)
// setTimeout(() => {
//   trafficLight.pause()
// }, 5 * 1000)

// note: 继续(模拟按钮的点击,人工操作)
// setTimeout(() => {
//   trafficLight.turnon()
// }, 10 * 1000)

// 监听灯变化的状态
trafficLight.on('change', e => {
  console.log('change:', e.currentColor)
})