你好,JavaScript異步編程---- 理解JavaScript異步的美妙

潛心修煉一段時間的我又回來了

天天不能不寫業務,但也不能只寫業務。因此選擇了一個本身還在學校期間沒有學明白的內容進行了鞏固,同步異步問題。學習一個知識以前必先給本身一個問題三連。爲何要有這個? 這個怎麼用? 這個怎麼回事?前端

爲何要有同步異步?

首先JS是一個單線程的語言。單線程的含義相似於從頭走到尾,誰也別管誰,前面堵車我就停(官方:單線程在程序執行時,所走的程序路徑按照連續順序排下來,前面的必須處理好,後面的纔會執行。),沒辦法開多個線程。java

疑問來了?爲何JS不設計成多線程的能夠開多個線程同時操做。JS是能夠去操做DOM的。假設JS設計成一個多線程語言。你的主線程在給DOM的innerHtml作一個賦值操做,你的另外一個線程把這個DOM結構刪除了。。。。這確定不能夠。(多線程能夠互不干預的操做一段內存空間)。因此乾脆設計成一個單線程,哪怕後期HTML5出現的web worker也是不容許操做DOM結構的,能夠完成一些分佈式的計算。對於dom結構咱們必須順序操縱,堅定不容許出現對同一個dom同時進行操做。node

可是瀏覽器加載一些須要網絡請求的好比圖片資源、ajax。或者輪訓的內容。因爲是單線程,須要等待這些內容訪問完才能夠執行下面的代碼。那麼你發個ajax請求或者請求個圖片資源,那麼這段時間就什麼也作不了,這種效果對程序是一種阻塞,在等待時間明明能夠作些別的事情卻選擇了無心義的等待。(同步阻塞)這個時候異步就出現了,在涉及須要等待的操做,咱們選擇讓程序繼續運行,在等待時間結束的時候,通知一下咱們的程序內容執行完畢,你能夠操做這些資源了,這段等待時間並不影響你程序的繼續執行,只是在將來的某個時間段(不肯定),有一個操做必定會執行,這就是異步。(異步非阻塞),這就是爲何要有同步異步。web

基礎的內容咱們講解完畢,下面開始真正的乾貨

(第一次瞭解這個的,請先補習一下定時器與promise的知識)ajax

xxxxxxxxx(一堆不肯定的代碼)

setTimeout(()=>{console.log(111)},500);
複製代碼

請問打印出111會在何時? 答案是500ms的時候打印算法

錯!答案是500ms或者500ms之後的某個時間段。promise

首先碰見定時器後,會將定時器內的函數進行註冊,也就是放入Event Table 。而後在500ms後將Event Table內註冊的函數放入 Event queue。若主線程(我也就一個線程)中的call stack(調用堆棧,也就是線程中函數的一個調用棧)爲空就將Event queue按順序的放入call stack中進行執行。若是call stack並不爲空, Event queue內的事件並不會進入call stack中,也就不會執行。瀏覽器

怎麼忽然來了這麼多亂七八糟,畫風和剛纔不同呀。難度上來了。網絡

首先對於異步事件,咱們在執行到這行代碼的時候會進行一個註冊,將你要在將來某個時間段要執行的函數註冊一下,放在Event table中。這個Event table中能夠有不少事件,好比你一次發了好多ajax請求,那麼他們就所有註冊了。在將來的時間到了,就會把註冊的事件放入Event queue(任務隊列)這個任務隊列就是立刻要執行的內容。多線程

任務隊列何時能夠執行?在主線程的call stack爲空的時候,會把任務隊列的第一個事件放入call stack中執行,這裏面涉及一個queue(隊列)的特色就是先進先出。在註冊後先放入Event queue的事件就會更早的離開Event queue進入主線程執行。這個時候是否是以爲本身明白點了? 唉別高興的太早了。

來吧 聊一聊宏任務與微任務吧

setTimeout(()=>{console.log(111)},0)

let promise = new Promise((resolve,reject)=>{
	console.log(222);
	resolve(3333)
});

let promise2 = new Promise((resolve,reject)=>{
	console.log(555);
	resolve(6666)
});

setTimeout(()=>{
	console.log(4444);
},0)

promise.then(res=>{
	console.log(res);
});

promise2.then(res=>{
	console.log(res);
});
複製代碼

你說這會打印出個什麼? 按理論先進先出,222 555 111 4444 333 666 你看對不對。 事實證實:222 555 333 666 111 4444 這是爲何? 首先註冊的事件也有不一樣形態,宏任務與微任務。

常見的宏任務:setTimeout、setInterval(定時器類)

常見的微任務:promise process.nextTick(一個node環境的方法)。

這兩個任務的執行規則是什麼?首先在call stack中的內容執行完畢清空後,會在Event queue檢查一下哪些是宏任務哪些是微任務,而後執行全部的微任務,而後執行一個宏任務,以後再次執行全部的微任務。也就是說在主線程任務執行完畢後會把任務隊列中的微任務所有執行,而後再執行一個宏任務,這個宏任務執行完再次檢查隊列內部的微任務,有就所有執行沒有就再執行一個宏任務。

爲何我一行獨立的異步的代碼也寫不出來

在這個概念理解清楚以後,我大約明白一些了,但是我就是一行真正的脫離了定時器、事件、ajax的異步的代碼也寫不出來,我一直在想如何實現消息通知異步的消息通知?觀察者模式嗎?那也沒辦法模擬出來,10分鐘以後我要作一個任務。想了好久。 在有一天需求評審中,咱們要作一個定時提醒的需求,這個時候後臺的任務分配中選擇了幾天來製做定時任務。這個時候我理解了。java的定時提醒是要開一個新的線程去不斷輪巡時間,能夠設置的間隔可是間隔越小,越消耗機器的性能。後來查閱資料瞭解,JS是單線程,可是瀏覽器不是,只是執行JS代碼的引擎是個單線程的因此JS的代碼沒辦法開啓多個線程,可是瀏覽器還有定時器線程、事件觸發線程、異步http請求線程、GUI線程。

因此在執行定時器、事件、ajax這些異步事件的時候是另外三個線程在執行代碼,並非JS引擎在作事情,在這些線程達到某一特定事件把任務放入JS引擎的線程中,同時GUI線程(渲染界面HTMl的線程)與JS線程是互斥的,在JS引擎執行時GUI線程會被凍結、掛起。

最後最後 JS是單線程可是瀏覽器是多線程。你的異步任務是瀏覽器開啓對應的線程來執行的,最後放入JS引擎中進行執行。

未完待續

今天基本理解了JS的異步任務是什麼意思。下一節(多是下週或者大下週) 我就要針對於Promise 與async/await 以及回調地獄來進行梳理與學習了。下次仍是這個主題咱們不見不散。

內容若有不當之處,歡迎各位大佬,評論區指點~~~

最後打個廣告(咱們一塊兒維護的學習公衆號)

公衆號主要面向的是初級/應屆生。內容包含咱們從應屆生轉換爲職場開發所踩過的坑,以及咱們每週的學習計劃和學習總結。 內容會涉及計算機網絡算法等基礎;也會涉及前端,後臺,Android等內容~

求關注,不迷路

咱們基友團其餘朋友的文章:

Android基友

Java基友

相關文章
相關標籤/搜索