題目:紅燈三秒亮一次,綠燈一秒亮一次,黃燈2秒亮一次;如何讓三個燈不斷交替重複亮燈?(用Promse實現)三個亮燈函式已經存在:
1 2 3 4 5 6 7 8 9 |
function red(){ console.log('red'); } function green(){ console.log('green'); } function yellow(){ console.log('yellow'); } |
這道題首先考察Promise的應用,Promise的詳細說明請看我的這篇文章:閒話Promise機制。首先我們需要一個函式來實現時間控制:
1 2 3 4 5 6 7 8 |
var tic = function(timmer, cb){ return new Promise(function(resolve, reject) { setTimeout(function() { cb(); resolve(); }, timmer); }); }; |
如果把問題簡化一下,如果只需要一個週期,那麼利用Promise應該這樣寫:
1 2 3 4 5 6 7 8 9 10 |
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
這三個,比如:
1 2 3 4 5 6 7 8 9 10 11 12 |
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的函式永遠不會執行!
正確的解決方法就是這道題的第三個考查點——遞迴!!!解決方案如下:
1 2 3 4 5 6 7 8 9 10 11 12 |
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); }); } |
整體程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
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
來減少回撥:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
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); |