更新:增加使用 async/await
以及箭頭函數的重構代碼
問題
紅燈三秒亮一次,綠燈一秒亮一次,黃燈2秒亮一次;如何讓三個燈按照這個規律不斷交替重複亮燈?(用Promse實現)三個亮燈函數已經存在:
function green() {
console.log("green");
}
function yellow() {
console.log("yellow");
}
function red() {
console.log("red");
}
#思路解析
一道經典的考察 promise 應用的面試題
我們要解決兩個問題,思路如下
###1 控制每盞燈,使其按某節奏閃爍
- 命名爲 flash 函數,參數爲亮燈函數與時間參數
- 應該會用到計時器
###2 控制三種燈閃爍的順序
- 命名爲 control 函數。
- 應該會用到promise,鏈式調用。
###3 實現重複不斷的循環
- 遞歸
- 爲什麼不能使用 while 等實現無限循環
#代碼演進
實現一次週期內的閃爍
通過上面 1、2 兩點,可以得到如下代碼
function flash(cb, time) {}
function control() {
Promise.resolve()
.then()
.then()
.then()
}
對於 control
函數
promise.then() 方法:
- 期望的參數爲函數
- 返回的結果是一個promise
故而,then 方法的參數函數爲
function() {
return flash(red, 3000);
}
control
函數整體爲
function control(promise) {
promise
.then(function() {
return flash(red, 3000);
})
.then(function() {
return flash(green, 1000);
})
.then(function() {
return flash(yellow, 2000);
});
}
flash
函數
由上可知,它應該返回一個完成態的 promise。
由於要結合定時器使用,採用promise構造函數的寫法,代碼如下
function flash(cb, time) {
return new Promise(function executor(resolve, reject) {
setTimeout(() => {
cb(), resolve();
}, time);
});
}
目前,已經實現了一個週期內的亮燈控制。
實現重複閃爍
要實現不斷重複,考慮遞歸
爲什麼 while
不行
當然,說到無限循環,很容易想到 while
循環等方法。事實上,這種辦法行不通。這涉及到 JavaScript 異步運行機制,參考 單線程 JavaScript 的異步機制與經典 for 循環面試題 。
簡單地說
-
一次 事件循環 中,同步代碼先入 執行棧 執行,異步代碼分情況將其任務註冊到 任務隊列 中。
-
只有執行棧清空,主線程 纔會從任務隊列中讀取任務,使其入棧執行
-
類似
while(true)
這樣的代碼,會永久佔據主線程,使得執行棧永遠不清空。 -
所以,任務隊列中等待的亮燈任務也就永無出頭之日。
遞歸
燈光閃爍一個週期,即 control
函數運行一次之後,讓 control
函數遞歸調用自身即可。
還是用 then 方法控制流程,加一個 .then()
,其參數按規定是一個函數,在該匿名函數內部調用函數本身,實現遞歸。
control
函數最終如下
function control() {
Promise.resolve()
.then(function() {
return flash(red, 3000);
})
.then(function() {
return flash(green, 1000);
})
.then(function() {
return flash(yellow, 2000);
})
.then(function() {
control();
});
}
#完整代碼
function green() {
console.log("green");
}
function yellow() {
console.log("yellow");
}
function red() {
console.log("red");
}
function flash(cb, time) {
return new Promise(function executor(resolve, reject) {
setTimeout(() => {
cb(), resolve();
}, time);
});
}
function control() {
Promise.resolve()
.then(function() {
return flash(red, 3000);
})
.then(function() {
return flash(green, 1000);
})
.then(function() {
return flash(yellow, 2000);
})
.then(function() {
control();
});
}
control();
重構
const red = () => console.log('red')
const green = () => console.log('green')
const yellow = () => console.log('yellow')
const controlLight = color => time => (
new Promise((resolve, reject) => {
setTimeout(() => {
color()
resolve()
}, time)
})
)
const controlOrder = async () => {
await controlLight(red)(3000)
await controlLight(green)(1000)
await controlLight(yellow)(2000)
await controlOrder()
}
controlOrder()