話很少說,首先咱們確定要實現紅綠燈的展現,這部分比較基礎,直接上代碼編程
// CSS部分
div {
background-color: gray;
display: inline-block;
margin: 30px;
height: 100px;
width: 100px;
border-radius: 50%;
}
.green.light {
background-color: green;
}
.yellow.light {
background-color: yellow;
}
.red.light {
background-color: red;
}
// HTML部分
<div class="green"></div>
<div class="yellow"></div>
<div class="red"></div>
// JS部分
function green() {
let lights = document.getElementsByTagName("div");
for (let i = 0; i < 3; i++) {
lights[i].classList.remove("light");
document.getElementsByClassName("green")[0].classList.add("light");
}
}
function yellow() {
let lights = document.getElementsByTagName("div");
for (let i = 0; i < 3; i++) {
lights[i].classList.remove("light");
document.getElementsByClassName("yellow")[0].classList.add("light");
}
}
function red() {
let lights = document.getElementsByTagName("div");
for (let i = 0; i < 3; i++) {
lights[i].classList.remove("light");
document.getElementsByClassName("red")[0].classList.add("light");
}
}
複製代碼
接下來,咱們先思考一下如何每隔一段時間去點亮一個燈,而且讓其餘燈變灰。最簡單的辦法就是用setTimeout()去實現promise
function go() {
green();
setTimeout(() => {
yellow();
setTimeout(() => {
red();
setTimeout(() => {
go()
}, 5000)
}, 2000);
}, 10000);
}
go();
複製代碼
剛開始我用的是setInterval()去實現循環的,可是它有一個最大的弊端就是須要寫間隔的總時間,相對而言,並無遞歸來得簡潔優雅。
這裏咱們也能夠看到,用setTimeout去實現的話就是無腦嵌套,可是須要循環的元素不少的話,就會陷入「回調地獄」,「地獄模式啊,筒子們!」
爲了幫助你們擺脫「地獄」,回到「人間」,ES6將promise寫入了規範,promise最大的優點就是採用鏈式調用,解決了回調地域問題。bash
function sleep(t) {
return new Promise((resolve, reject) => {
setTimeout(resolve, t);
})
}
function go() {
green();
sleep(10000).then(() => {
yellow();
return sleep(2000);
}).then(() => {
red();
return sleep(5000);
}).then(go).catch(err => {
console.log('出錯啦!')
});
}
go();
複製代碼
函數會根據上一個promise返回的執行結果(resolve,或者reject),來決定繼續執行then裏面的代碼仍是執行catch裏面的代碼。
promise相對於setTimeout來講,明顯的避免了「回調地獄」問題,可是 也有弊端,最直白的就是有不少then,使代碼很是冗餘,不夠簡潔和語義化。 那麼怎麼幹掉then,將異步代碼假裝的像同步代碼呢?前輩們採用generator函數去解決這個問題。框架
function sleep(t) {
return new Promise((resolve, reject) => {
setTimeout(resolve, t);
});
}
function* go() {
while(true) {
green();
yield sleep(10000);
yellow();
yield sleep(2000);
red();
yield sleep(5000);
}
}
複製代碼
可是generator的調用就須要藉助co框架去實現了,下面是co框架的實現思路異步
function run(iterator) {
let {value, done} = iterator.next();
if (done) {
return;
}
if (value instanceof Promise) {
value.then(() => {
run(iterator);
})
}
}
function co(generator) {
return function() {
return run(generator());
}
}
go = co(go);
go();
複製代碼
咱們能夠看到使用generator函數確實更加語義化了,可是須要引進co框架,你可能會想:「就一個紅綠燈問題我還得引進一個co框架,內啥,我40米的大刀呢?」
ES2017 標準引入了 async 函數,使得異步操做變得更加方便。async 函數是什麼?一句話,它就是 Generator 函數的語法糖。 接下來咱們用async函數來實現紅綠燈問題async
function sleep(t) {
return new Promise((resolve, reject) => {
setTimeout(resolve, t);
})
}
async function go() {
while(true) {
green();
await sleep(10000);
yellow();
await sleep(2000);
red();
await sleep(5000);
}
}
go();
複製代碼
看到這裏,你是否是有一種「刪繁就簡」爽快感。沒有了被「回調地獄」支配的恐懼,也沒有了then的冗餘,異步代碼以同步的方式優雅的呈現了出來。
若是你們發現本文的代碼有錯誤或疏漏之處,歡迎你們指正。異步編程