題目:紅燈三秒亮一次,綠燈一秒亮一次,黃燈2秒亮一次;如何讓三個燈不斷交替重復亮燈?(用Promse實現)
三個亮燈函數已經存在:
function red(){
console.log('red');
}
function green(){
console.log('green');
}
function yellow(){
console.log('yellow');
}
這道題首先考察Promise的應用,Promise的詳細說明請看我的這篇文章:閑話Promise機制。首先我們需要一個函數來實現時間控制:
var tic = function(timmer, cb){
return new Promise(function(resolve, reject) {
setTimeout(function() {
cb();
resolve();
}, timmer);
});
};
如果把問題簡化一下,如果只需要一個周期,那么利用Promise應該這樣寫:
var d = new Promise(function(resolve, reject){resolve();});
var step = function(def) {
def.then(function(){
return tic(3000, red);
}).then(function(){
return tic(2000, green);
}).then(function(){
return tic(1000, yellow);
});
}
現在一個周期已經有了,剩下的問題是如何讓他無限循環。說道循環很容易想到for
while
do-while
這三個,比如:
var d = new Promise(function(resolve, reject){resolve();});
var step = function(def) {
while(true) {
def.then(function(){
return tic(3000, red);
}).then(function(){
return tic(2000, green);
}).then(function(){
return tic(1000, yellow);
});
}
}
如果你是這樣想的,那么恭喜你成功踩了坑!這道題的第二個考查點就是setTimeout相關的異步隊列會掛起知道主進程空閑。如果使用while無限循環,主進程永遠不會空閑,setTimeout的函數永遠不會執行!
正確的解決方法就是這道題的第三個考查點——遞歸!!!解決方案如下:
var d = new Promise(function(resolve, reject){resolve();});
var step = function(def) {
def.then(function(){
return tic(3000, red);
}).then(function(){
return tic(2000, green);
}).then(function(){
return tic(1000, yellow);
}).then(function(){
step(def);
});
}
整體代碼如下:
function red(){
console.log('red');
}
function green(){
console.log('green');
}
function yellow(){
console.log('yellow');
}
var tic = function(timmer, cb){
return new Promise(function(resolve, reject) {
setTimeout(function() {
cb();
resolve();
}, timmer);
});
};
var d = new Promise(function(resolve, reject){resolve();});
var step = function(def) {
def.then(function(){
return tic(3000, red);
}).then(function(){
return tic(2000, green);
}).then(function(){
return tic(1000, yellow);
}).then(function(){
step(def);
});
}
step(d);
同時可以看到雖然Promise可以用來解決回調地獄問題,但是仍然不可避免的會有回調出現,更好的解決方案是利用Generator
來減少回調:
var tic = function(timmer, str){
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log(str);
resolve(1);
}, timmer);
});
};
function *gen(){
yield tic(3000, 'red');
yield tic(1000, 'green');
yield tic(2000, 'yellow');
}
var iterator = gen();
var step = function(gen, iterator){
var s = iterator.next();
if (s.done) {
step(gen, gen());
} else {
s.value.then(function() {
step(gen, iterator);
});
}
}
step(gen, iterator);