JS实现一个可以配置的红绿灯

111 篇文章 1 订阅
62 篇文章 1 订阅

转载——https://juejin.im/post/5c76357f51882540713f5717

【javascript】一个可以配置的红绿灯

需求

实现一个信号灯,这个信号灯,有黄绿红,他们各自亮灯的持续时间是 1s,2s,3s 如此反复。

前景提要,我们的html代码是这样:

<!DOCTYPE html>
<html lang="en">
   <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <meta http-equiv="X-UA-Compatible" content="ie=edge">
     <title>红绿灯</title>
     <style>
       #circle {
         background-color: green;
         border-radius: 50%;
         width: 100px;
         height: 100px;
       }
     </style>
   </head>
   <body>
     <div id="circle"></div>
   </body>
</html>

复制代码

通过 setTimeout来实现

我们通过setTimeout或者setInterval来实现:

   function setColor(color) {
       document.getElementById("circle").style.backgroundColor = color
   }

   function startWork() {
     setColor("green")
     setTimeout(() => {
         setColor("yellow")
         setTimeout(() => {
           setColor("red")
           setTimeout(() => {
             startWork()
           }, 4000)
         }, 3000)
     }, 2000)
   }
   
   startWork()

复制代码

通过promise来实现

这样是可行的,但是这个回调看起来让人抓狂,我们用promise来实现这一版本

   function delay(duration) {
     return new Promise((resolve, reject) => {
         setTimeout(resolve, duration)
     })
   }
 
 
   function changeColor(color, duration) {
       const func = function () {
           document.getElementById("circle").style.backgroundColor = color
       }
       func()
       return delay(duration)
   }
   
   function startWork() {
     return Promise.resolve()
       .then(() => changeColor("green", 2000))
       .then(() => changeColor("yellow", 3000))
       .then(() => changeColor("red", 4000))
       .then(() => startWork())
   }
   
   startWork()

复制代码

通过async进行优化

但是感觉这样还是不够优雅,我们用async做一下优化函数startWork:

    async function startWork() {
       await changeColor("green", 2000)
       await changeColor("yellow", 3000)
       await changeColor("red", 4000)
       startWork()
   }
复制代码

没错,这样更简洁了,但是到目前为止,但是我们想实现暂停和开始,停止和重新开始。 其实我一开始想的,停止的话,最好是循环,break出循环即可。那么我们可以循环来实现这个一直重复的动作呢,答案是可以的。

好了 核心来了

通过Symbol.iterator改良promise/async版本

我们封装到一个类里面,把这个一直持续的过程,通过自定义的迭代器来实现:

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

   class TrafficLight {
       constructor(initStates) {
           this.stateLength = initStates.length
           this.states = initStates
       }
       *[Symbol.iterator]() {
           const max = Number.POSITIVE_INFINITY
           const states = this.states
           for (let i = 0; i < max; i++) {
               const index = i % this.stateLength
               yield this.states[index]
           }
       }
   }

   var trafficLight = new TrafficLight([
       {
           color: "green",
           duration: 2000
       },
       {
           color: "yellow",
           duration: 3000
       },
       {
           color: "red",
           duration: 4000
       }
   ])
   var startWork = async function() {
       for(let item of trafficLight ) {
           document.getElementById("circle").style.backgroundColor = item.color
           await delay(item.duration)
       }
   }
   startWork()
复制代码

在自定义迭代里面去控制暂停

已经到这一步了,你知道怎么实现 暂停 和 恢复了吗?如果不知道,建议你继续想一下~

我们已经封装了一个信号灯类,这个实例有一个初始化的数组对象,它保存了信号灯的几种状态。我们也自定义了迭代器。

我们需要一个flag,这个flag用来标记是否是暂停状态,如果是暂停状态,我们保存当前的index,并且在自定义迭代器里面执行break[那么此次迭代就停止了实际上]。我们恢复操作的时候,因为已经记录了从哪一个index开始停止的,我们恢复方法要做的操作就是:从该index开始遍历即可。

啊,最后的 代码长下面这样:

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

    class TrafficLight {
        constructor(initStates) {
            this.stateLength = initStates.length
            this.states = initStates
            this.startIndex = 0  // 暂停之后保存的index,需要存放在这里
            this.pauseStatus = false
        }
        setStartIndex(index) {
          this.startIndex = index
        }
        setStartIndexToZero() {
          this.startIndex = 0
        }
        pauseProcess() {
          this.pauseStatus = true
        }
        recoveryProcess() {
          this.pauseStatus = false
          this.startWork()
        }
        async startWork() {
          for(let item of this ) {
            // 操作可以封装到函数,传递进来
            document.getElementById("circle").style.backgroundColor = item.color
            await delay(item.duration)
          }
        }
        *[Symbol.iterator]() {
            const max = Number.POSITIVE_INFINITY
            const states = this.states
            for (let i = this.startIndex; i<max; i++) {
                const index = i % this.stateLength
                // 在这里控制暂停 or 停止
                // core code
                if (this.pauseStatus) {
                  this.startIndex = index === 0 ? (this.stateLength - 1) : (index - 1)
                  break
                }
                yield this.states[index]
            }
        }
    }

    var trafficLight = new TrafficLight([
        {
            color: "green",
            duration: 2000
        },
        {
            color: "yellow",
            duration: 3000
        },
        {
            color: "red",
            duration: 4000
        }
    ])

    trafficLight.startWork()

复制代码

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值