手摸手,帶你探究Javascript異步編程

從一個紅綠燈問題來學習異步編程

問題描述:一個路口的紅綠燈稽亏,會按照你綠燈亮10秒壶冒,黃燈亮2秒,紅燈亮5秒的順序無限循環(huán)截歉,請編寫JS代碼來控制這個紅綠燈

話不多說胖腾,首先我們肯定要實現紅綠燈的展示,這部分比較基礎瘪松,直接上代碼

// 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()去實現

function go() {
    green();
    setTimeout(() => {
        yellow();
        setTimeout(() => {
            red();
            setTimeout(() => {
                go()
            }, 5000)
        }, 2000);
    }, 10000);
}

go();

剛開始我用的是setInterval()去實現循環(huán)的记罚,但是它有一個最大的弊端就是需要寫間隔的總時間,相對而言壳嚎,并沒有遞歸來得簡潔優(yōu)雅桐智。
這里我們也可以看到末早,用setTimeout去實現的話就是無腦嵌套,但是需要循環(huán)的元素很多的話说庭,就會陷入“回調地獄”然磷,“地獄模式啊,筒子們刊驴!”
為了幫助大家擺脫“地獄”姿搜,回到“人間”,ES6將promise寫入了規(guī)范缺脉,promise最大的優(yōu)勢就是采用鏈式調用痪欲,解決了回調地域問題。

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返回的執(zhí)行結果(resolve,或者reject),來決定繼續(xù)執(zhí)行then里面的代碼還是執(zhí)行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函數來實現紅綠燈問題

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的冗余桩蓉,異步代碼以同步的方式優(yōu)雅的呈現了出來淋纲。
如果大家發(fā)現本文的代碼有錯誤或疏漏之處,歡迎大家指正院究。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末洽瞬,一起剝皮案震驚了整個濱河市本涕,隨后出現的幾起案子,更是在濱河造成了極大的恐慌伙窃,老刑警劉巖菩颖,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異为障,居然都是意外死亡晦闰,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門鳍怨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來呻右,“玉大人,你說我怎么就攤上這事鞋喇∩模” “怎么了?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵侦香,是天一觀的道長落塑。 經常有香客問我,道長罐韩,這世上最難降的妖魔是什么憾赁? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮散吵,結果婚禮上龙考,老公的妹妹穿的比我還像新娘。我一直安慰自己矾睦,他們只是感情好洲愤,可當我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著顷锰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪亡问。 梳的紋絲不亂的頭發(fā)上官紫,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天,我揣著相機與錄音州藕,去河邊找鬼束世。 笑死,一個胖子當著我的面吹牛床玻,可吹牛的內容都是我干的毁涉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼锈死,長吁一口氣:“原來是場噩夢啊……” “哼贫堰!你這毒婦竟也來了穆壕?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤其屏,失蹤者是張志新(化名)和其女友劉穎喇勋,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體偎行,經...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡川背,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了蛤袒。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片熄云。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖妙真,靈堂內的尸體忽然破棺而出缴允,到底是詐尸還是另有隱情,我是刑警寧澤隐孽,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布癌椿,位于F島的核電站,受9級特大地震影響菱阵,放射性物質發(fā)生泄漏踢俄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一晴及、第九天 我趴在偏房一處隱蔽的房頂上張望都办。 院中可真熱鬧,春花似錦虑稼、人聲如沸琳钉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽歌懒。三九已至,卻和暖如春溯壶,著一層夾襖步出監(jiān)牢的瞬間及皂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工且改, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留验烧,地道東北人。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓又跛,卻偏偏與公主長得像碍拆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,060評論 2 355