红绿灯大战之终结版

2015年4月10日,微博上爆发了波澜壮阔的“红绿灯大战”,这一天史称:红绿灯日。
@十年踪迹(真名吴亮,曾就职百度,《javascript王者归来》作者,现担任360前端主管)
在微博上发了个帖子:
过程抽象之promise化--用过程抽象的思路将一个普通的异步函数“变换”成promise形式
http://code.w3ctech.com/detail/30
随后@winter(真名田永强,现就职阿里,《深入浅出node.js》作者,在js领域造诣颇深)
表示不服,也写了个版本:http://code.w3ctech.com/detail/31
然后裕波同学(360浏览器主管)
让@aimingoo(真名沈爱民,现就职豌豆荚,也是前端大牛)
来评估谁的代码实现更好些。
然后@hax(贺师俊 现百姓网前端主管)和一堆爱好者也加入进来,写了好多个版本。
把路径最后的数字不停地加一,即可看到对应的版本。
总共有44个版本,数字从30到73。
专题可见:前端要给力之:红绿灯大战中的火星生命-Promise
感兴趣的同学都可以提交自己的版本,
我在偶然间灵感触发,写了属于自己的红绿灯大战之终结版。
我的实现思路与这44个版本截然不同,是基于我上篇文章的函数嵌套来实现的。
上篇文章我们实现的是:输入框的值两秒后变成0,再隔两秒后变成1,再隔两秒后变成2。
这和红绿灯岂不是很像,立马动手,代码如下:

js部分是我自己写的,html和css都是遵循之前的版本。 首先要为页面设计部分点个赞, 在切换红绿灯的过程中,li对应的样式没有任何变化,变化的反而是ul的class。 然后再根据css中的巧妙设计来切换对应li的样式。 这个实现要简洁得多,否则我们就要做很多dirty操作来实现。 js部分也很简单,看我上篇文章就明白了。 但是实现效果仅仅是绿灯-黄灯-红灯,然后就结束了,达不到我们想要的循环效果。 我怎样在红灯后重新开始这个流程呢? 尝试过在最外面定义一个函数,然后在结束时调用最外面的函数, 但js代码报错,不支持这种写法。 本来我已经要放弃了,准备采用promise的写法。 但是灵机一动,不停地循环可以试试setInterval啊。 简单摸索了下,代码更改如下:
setInterval(function(){
setTimeout(function(){
traffic.className='yellow';
setTimeout(function(){
traffic.className='red';
setTimeout(function(){
traffic.className='green';
},500)
},500)
},500)
},1500)

perfect!
诀窍:setInterval的时间设置必须等于里面三个参数总和,这样红绿灯循环就非常流畅。
实现原理:
最外面的定时器用来控制每隔1500毫秒重复里面的红灯绿切换流程,
当切换到绿灯时,刚好花了1500毫秒,此时最外面的定时器开始生效,自动重新开始这个流程。
而不需要在绿灯后手动重新调用整个流程。
这一点是整个代码的精髓所在,如果不能转化思路,停留在最里面如何调用最外面,就无法实现。
让我们再来看看用promise实现的最简洁版本(@米粽粽同学基于版本41的实现改进后的版本51)

function turn(color, duration) {
return new Promise(function(resolve, reject) {
traffic.className = color
setTimeout(resolve, duration)
})
}
void function run() {
turn('green', 500)
.then(turn.bind(null, 'yellow', 500))
.then(turn.bind(null, 'red', 500))
.then(run)
}()

我的实现,11行代码,函数嵌套四层,没有任何新概念,技巧,掌握js基础即可。
promise的实现,12行代码,函数嵌套二层,多种新概念,技巧,需要掌握很多知识。
一种截然不同的实现方式,一种我差点就要放弃的实现方式。
实现之后你会觉得我写的代码很简单,没有任何难度,但需要你对定时器非常熟悉。
所以我把这称作红绿灯大战之终结版,等你来挑战哦!看你能不能写出更简洁的版本。

28日最新更新:

在最里面是可以调用最外面的,之前是我写的代码有问题,见:

void function run(){
setTimeout(function(){
traffic.className='yellow';
setTimeout(function(){
traffic.className='red';
setTimeout(function(){
traffic.className='green';
run();
},500)
},500)
},500)
}()

这又是一种不同的实现方式,也很简洁,12行代码而已。
这么多版本,我最喜欢11行代码的那个版本,你呢?