JS異步編程(1)-歷史演進(jìn)

異步編程誕生的原因

JavaScript 在 1992 年發(fā)布

這里致敬一下 JavaScript 主要?jiǎng)?chuàng)造者與架構(gòu)師,布蘭登·艾克

感謝“祖師爺”賞的飯碗

JS的單線程

JS設(shè)計(jì)的初衷是為了表單校驗(yàn)和 dom 操作
為了防止一個(gè)線程對(duì)dom操作時(shí),另一個(gè)線程刪除這個(gè)dom ,因此將其設(shè)計(jì)為單線程

單線程的優(yōu)缺點(diǎn)

單線程的模式有它的好處,但同時(shí)也帶來(lái)了問(wèn)題,那就是阻塞

  • 同步運(yùn)行:?jiǎn)尉€程意味著兩段代碼不能同時(shí)運(yùn)行,而是必須逐步地運(yùn)行
  • 阻塞操作:如果有非常耗時(shí)的任務(wù),會(huì)出現(xiàn)用戶長(zhǎng)時(shí)間等待,并且在當(dāng)前任務(wù)完成前,其他操作都無(wú)法響應(yīng)的情況

所以在同步代碼執(zhí)行過(guò)程中,需要將這類耗時(shí)的任務(wù)進(jìn)行異步處理
避免阻塞正常的邏輯執(zhí)行

JS異步編程的核心原理

JS異步編程的核心是 Event loop
Web 端和 Node 端各有不同

在這里不是主要內(nèi)容,簡(jiǎn)單描述一下

Web 端

Event loop 是由 HTML5 規(guī)范明確定義,由各大瀏覽器廠商各自實(shí)現(xiàn)的一套 JavaScript 在瀏覽器環(huán)境下的事件循環(huán)機(jī)制

Node端

Nodejs 的 Event loop 是基于 libuv,并且 libuv 已經(jīng)對(duì) Event loop 作出了實(shí)現(xiàn)

階段一:回調(diào)函數(shù)

最基本也是最原始的異步編程模式就是回調(diào)函數(shù)

// taks1 -> cb1 -> task2 -> cb2 -> task3 -> cb3
task1(function cb1() {
    task2(function cb2() {
        task3(function cb3() {
            cb3()
        })
    })
})

回調(diào)函數(shù)給人一種什么樣的感覺(jué)?像什么?
像是俄羅斯套娃,大的套小的,隨著套娃越來(lái)越多。某一天,我突然想把最里面的一個(gè)拿出來(lái),這時(shí)候就絕望了。

這說(shuō)明伴隨著回調(diào)函數(shù)的嵌套增加,帶來(lái)了一些問(wèn)題,比如:

  • 修改成本過(guò)高
    • 當(dāng)我們需要修改回調(diào)函數(shù)時(shí),發(fā)現(xiàn)無(wú)從下手
  • 局部作用域嵌套
    • 由于使用函數(shù)嵌套,最內(nèi)層的函數(shù)擁有最大的作用域范圍。
    • 極有可能不小心重定義或者修改了上層作用域的變量或函數(shù)
    • 代碼混亂,不夠直觀

回調(diào)函數(shù)的嵌套問(wèn)題有一個(gè)統(tǒng)稱——回調(diào)地獄
為了解決回調(diào)地獄的問(wèn)題,到 ES6 發(fā)布,出現(xiàn)了第二種異步編程模式 Promise

階段二:Promise

Promise 是 ES6 中引入的新特性,與傳統(tǒng)回調(diào)函數(shù)寫法相比,有兩個(gè)區(qū)別:

  1. Promise 使用 then 函數(shù)進(jìn)行鏈?zhǔn)秸{(diào)用,不再是以往的那種嵌套結(jié)構(gòu)了
  2. 每個(gè) then 函數(shù)中的回調(diào)函數(shù)互相獨(dú)立,不再有作用域的干擾

將之前的例子改寫,可以看到代碼邏輯變得更加清晰

// taks1 -> then -> task2 -> then -> task3
task1()
    .then(function() {
        task2()
    })
    .then(function() {
        task3()
    })

但是 Promise 本身還是有一堆的 then 函數(shù),then函數(shù)中還是寫了一堆的回調(diào)函數(shù)
依舊不能讓我們像寫同步代碼一樣寫異步的代碼,更像是一個(gè)偽同步寫法

這個(gè)時(shí)候同樣伴隨 ES6 發(fā)布的 Generator 提供了一種思路

階段三:Generator

Generator 也是 ES6 引入的新特性,原本是為了實(shí)現(xiàn)一種新的狀態(tài)機(jī)制管理
為我們提供控制函數(shù)執(zhí)行階段的能力

function* task1() {
    yield task2()
    yield task3()
}
let result = task1() // task1
result.next() // task2 返回值
result.next() // task3 返回值

這段示例代碼向我們展示 Generator 的幾個(gè)特點(diǎn)

  1. 必須使用 * 來(lái)聲明
  2. 使用 yield 關(guān)鍵字,使得函數(shù)內(nèi)部寫法真正像是同步任務(wù)
  3. 可中斷執(zhí)行,但需要手動(dòng)執(zhí)行next,否則后續(xù)代碼不會(huì)執(zhí)行

但還不夠,我們看 Generator 有什么樣的問(wèn)題

  1. 繁瑣的 next 方法調(diào)用
  2. 晦澀難懂的函數(shù)語(yǔ)義,單純看 * 和 yield,誰(shuí)能明白它要干嘛
  3. 用 Generator 來(lái)進(jìn)行異步編程,不是開箱即用。Generator 本身和異步編程無(wú)關(guān),但在使用過(guò)程中發(fā)現(xiàn)在異步編程中有巨大的價(jià)值,基本需要進(jìn)行較為完善的二次封裝(增加執(zhí)行器),才能成為一種異步編程模式,例如 co 庫(kù)
npm install co
let co = require('co')

function* task1() {
  yield task2()
  yield task3()
}
co(task1())

我們希望它能夠更簡(jiǎn)單直接一點(diǎn),然后 Async/Await 隆重登場(chǎng)了

階段四:Async/Await

Async 是 ES8 中引入的新特性,是 Generator 的語(yǔ)法糖
可以近似的認(rèn)為是 Generator + 執(zhí)行器 + Promise 的封裝

同樣修改一下上面的例子

// task1 -> task2 -> task3
async task1() {
    await task2()
    await task3()
}

優(yōu)點(diǎn)很明顯:

  • 語(yǔ)義化清晰明確:Async 異步,Await 等待,沒(méi)有歧義。其實(shí)大家也看的出來(lái),就是把 * 和 yield 換了一下
  • 同步任務(wù)的寫法:這點(diǎn)上也沿用了 Generator 的設(shè)計(jì)
  • 開箱即用:專門為異步編程設(shè)計(jì),不需要像 Generator 進(jìn)行二次封裝(執(zhí)行器)
  • Async / Await 可以嵌套使用
  • 隱式返回 Promise:可直接使用 Promise Api,進(jìn)行并發(fā)異步等模式的開發(fā)

綜合來(lái)看
Async/Await 是目前為止最完善的異步編程解決方案,解決了之前的痛點(diǎn)

總結(jié)

我們從時(shí)間線上來(lái)看 JavaScript 異步編程的演進(jìn)過(guò)程

time.png

ES6 以前,無(wú)論是事件監(jiān)聽、發(fā)布訂閱還是定時(shí)器,使用的還是原始的 Callback 方式

2015年 ES6 正式發(fā)布,同時(shí)將 Promise 和 Generator 引入標(biāo)準(zhǔn)。但是在社區(qū),Promise 和 Generator 都早有自己的雛形,Promise 的概念出現(xiàn)的時(shí)間相對(duì)而言還要更早一些。
所以在這條時(shí)間線上,我把他們兩個(gè)都定為 ES6 的正式發(fā)布的時(shí)間,但是 Promise 處在更早的時(shí)間節(jié)點(diǎn)

到了 2017 年 ES8 發(fā)布,Async 引入標(biāo)準(zhǔn),成為最新的解決方案,異步編程帶來(lái)的問(wèn)題告一段落。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容