转载——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()
复制代码