突然想起来之前我面试时候的一道面试题,用js实现一个红绿灯,想想当时发挥的不如人意,今天就来回顾复习一下。

题目

红灯持续5s,黄灯2s,绿灯3s,红灯5s后变黄灯2s后变绿灯3s后然后变红灯这样一直循环。要求:每一秒打印当前时哪个灯在亮?

解题方法

方法1——定时器递归

思路

封装一个函数专门打印入参内容,入参是红灯|黄灯|绿灯。
定义一个方法main(),main()内部执行:打印当前亮的等就用setInterval每秒执行打印,红灯打印setTimeout 5s后打印黄灯,再setTimeout 2s后打印绿灯再setTimeout 3s后清除定时器调用自己进行循环。

实现源码

  1. let timer = null
  2. const printContent = (str) => {
  3. console.log(str)
  4. }
  5. const main = () => {
  6. printContent('红灯')
  7. clearInterval(timer)
  8. timer = setInterval(() => {
  9. printContent('红灯')
  10. }, 1000)
  11. setTimeout(() => {
  12. printContent('黄灯')
  13. clearInterval(timer)
  14. timer = setInterval(() => {
  15. printContent('黄灯')
  16. }, 1000)
  17. setTimeout(() => {
  18. printContent('绿灯')
  19. clearInterval(timer)
  20. timer = setInterval(() => {
  21. printContent('绿灯')
  22. }, 1000)
  23. setTimeout(() => {
  24. clearInterval(timer)
  25. main()
  26. }, 3000)
  27. }, 2000)
  28. }, 5000)
  29. }
  30. main()

方法2——Promise实现

思路

封装一个sleep方法:用来在指定的时间段,每秒打印指定内容,入参是:时间和打印的内容。
然后在main方法中使用promise的then回调依次执行红、黄、绿灯,绿灯之后再调用main方法继续循环。

实现源码

let timer = null
const printContent = (str) => {
  console.log(str)
}

const sleep = (time, light) => {
  return new Promise((resolve, reject) => {
    printContent(light)
    clearInterval(timer)
    timer = setInterval(() => {
      printContent(light)
    }, 1000)
    setTimeout(resolve, time)
  })
}

const main = () => {
  sleep(5000, '红灯')
    .then(() => {
      return sleep(2000, '黄灯')
    })
    .then(() => {
      return sleep(3000, '绿灯')
    })
    .then(() => {
      main()
    })
    .catch(e => console.log(e))
}

main()

promise相对于setTimeout来说,明显的避免了“回调地狱”问题,但是 也有弊端,最明显的就是有很多then回调,使代码略显冗余,不够简洁和语义化。 ES2017 标准引入了 async 函数,它使得异步操作变得更加方便,接下来我们用async函数来实现一下这个红绿灯问题。

方法3——async/await实现

思路

封装一个sleep方法:用来在指定的时间段,每秒打印指定内容,入参是:时间和打印的内容。
和方法2的区别就是main方法,使用ES8的新特性,async/await实现异步。

实现源码

let timer = null
const printContent = (str) => {
  console.log(str)
}
const sleep = (time, light) => {
  return new Promise((resolve, reject) => {
    printContent(light)
    clearInterval(timer)
    timer = setInterval(() => {
      printContent(light)
    }, 1000)
    setTimeout(resolve, time)
  })
}

const main = async () => {
  while (true) {
    await sleep(5000, '红灯')
    await sleep(2000, '黄灯')
    await sleep(3000, '绿灯')
    clearInterval(timer)
  }
}

main()

用async函数实现起来确实很清爽,也提高了代码的可读性。没有了被“回调地狱”支配的恐惧,也避免了then回调的代码冗余,异步代码以同步的方式优雅的呈现了出来。