国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

泡杯茶,我們坐下聊聊Javascript的事件環(huán)

douzifly / 3405人閱讀

摘要:是怎么執(zhí)行的一開始先簡單聊了聊基本的數(shù)據(jù)結(jié)構(gòu),它和我們現(xiàn)在說的事件環(huán)有什么關(guān)系么當(dāng)然有,首先要明確的一點是,代碼的執(zhí)行全都在棧里,不論是同步代碼還是異步代碼,這個一定要清楚。

棧和隊列

在計算機內(nèi)存中存取數(shù)據(jù),基本的數(shù)據(jù)結(jié)構(gòu)分為棧和隊列。

棧(Stack)是一種后進先出的數(shù)據(jù)結(jié)構(gòu),注意,有時候也管棧叫做“堆棧”,但是“堆”又是另一種復(fù)雜的數(shù)據(jù)結(jié)構(gòu),它和棧完全是兩碼事。棧的特點是操作只在一端進行,一般來說,棧的操作只有兩種:進棧和出棧。第一個進棧的數(shù)據(jù)總是最后一個才出來。

隊列(Queue)和棧類似,但它是先進先出的數(shù)據(jù)結(jié)構(gòu),插入數(shù)據(jù)的操作從隊列的一端進行,而刪除的操作在另一端。

通俗的比喻棧就像是一個立好的桶,先放入棧的數(shù)據(jù)會放在桶底,出棧時會在桶口一一將數(shù)據(jù)取出,所以最先放入棧的數(shù)據(jù)總是最后一個才能取出。而隊列就像是一個水管,最先放入隊列的數(shù)據(jù)會第一個從隊列的另一端流出,這是它們最大的區(qū)別。

在javascript中,函數(shù)的執(zhí)行就一個典型的入棧與出棧的過程:

function fun1() {
    function fun2() {
        function fun3() {
            console.log("do it");
        }
        fun3();
    }
    fun2();
}
fun1();

在程序執(zhí)行時,首先將fun1,fun2,fun3依次入棧,而在調(diào)用函數(shù)時,是先將fun3調(diào)用(出棧),再是fun2和fun1,試想一下,如果fun1先出棧,那么函數(shù)fun2和fun3必將丟失。

單線程和異步

在javascript這門語言中程序是單線程的,只有一個主線程,這是為什么?因為不難想像,最初javascript的設(shè)計是跑在瀏覽器中的腳本語言,如果設(shè)計成多線程,兩個線程同時修改DOM那以誰的為準(zhǔn)呢?所以javascript為單線程,在一個線程中代碼會一句一句向下走,直到程序跑完,若中間有較為費時的操作,那也只能等著。

單線程的設(shè)計使得語言的執(zhí)行效率很差,為了利用多核心CPU的性能,javascript語言支持異步代碼,當(dāng)有較為費時的操作時,可將任務(wù)寫為異步執(zhí)行,當(dāng)一個異步任務(wù)還沒有執(zhí)行完時,主線程會將異步任務(wù)掛起,繼續(xù)執(zhí)行后面的同步代碼,之后再回過頭來看,如果有異步任務(wù)運行完了再執(zhí)行它。

這種執(zhí)行代碼的方式其實很符合我們生活中的很多場景,比如小明同學(xué)下班回家了,他很渴,想燒水泡茶,如果是同步的執(zhí)行方式那就是燒水,在水沒開時小明像個傻子似的等著,等水開了再泡茶;若是異步執(zhí)行,小明先開始燒水,然后就去干點別的事,比如看會電視、聽聽音樂,等水燒開了再去泡茶。明顯第二種異步方式效率更高。

常見的異步操作都有哪些?有很多,我們可以羅列幾個常見的:

Ajax

DOM的事件操作

setTimeout

Promise的then方法

Node的讀取文件

我們先來看一段代碼:

//示例1
console.log(1);
setTimeout(function () {
    console.log(2);
}, 1000);
console.log(3);

這段代碼非常簡單,把它們放在瀏覽器中執(zhí)行結(jié)果如下:

1
3
2

因為setTimeout函數(shù)延時了1000毫秒執(zhí)行,因此先輸出1和3,而2是過了1000毫秒之后再輸出,這很合邏輯。

我們稍稍改動一下代碼,將setTimeout的延時時間改為0:

//示例2
console.log(1);
setTimeout(function () {
    console.log(2);
}, 0); //0毫秒,不延時
console.log(3);

運行結(jié)果:

1
3
2

為什么延時了0毫秒還是最后輸出的2?先別急,我們再來看一段代碼:

//示例3
console.log(1);
setTimeout(function () {
    console.log(2);
}, 0);
Promise.resolve().then(function(){
    console.log(3);
});
console.log(4);

運行結(jié)果:

1
4
3
2

以上三段代碼,如果你能正確的寫出結(jié)果,并且能說明白為什么這樣輸出,說明你對javascript的事件環(huán)理解的很清楚,如果講不出來,我們就一起聊聊這里面發(fā)生了什么,其實很有意思。

javascript是怎么執(zhí)行的?

一開始先簡單聊了聊基本的數(shù)據(jù)結(jié)構(gòu),它和我們現(xiàn)在說的事件環(huán)有什么關(guān)系么?當(dāng)然有,首先要明確的一點是,javascript代碼的執(zhí)行全都在棧里,不論是同步代碼還是異步代碼,這個一定要清楚。

而代碼我們大體上分為了同步代碼和異步代碼,其實異步代碼還可以再分為兩類:宏任務(wù)微任務(wù)

先別管什么是宏任務(wù)和微任務(wù),往往這種高大上的術(shù)語不利于我們理解,我們先這么認(rèn)為:宏,即是宏觀的、大的;微即微觀的、小的。

javascript是解釋型語言,它的執(zhí)行過程是這樣的:

從上到下依次解釋每一條js語句

若是同步任務(wù),則壓入一個棧(主線程);如果是異步任務(wù),就放到一個任務(wù)隊列里

開始執(zhí)行棧里的同步任務(wù),直到將棧里的所有任務(wù)都走完,此時棧清空了

回過頭看異步隊列里如果有異步任務(wù)完成了,就生成一個事件并注冊回調(diào),壓入棧中

再返回第3步,直到異步隊列都清空,程序運行結(jié)束

語言描述的費勁,不如看圖:

通過以上的步驟可以看到,不論是同步還是異步,只要是執(zhí)行的時候都是要在棧里執(zhí)行的,而一遍又一遍的回頭檢查異步隊列,這種執(zhí)行方式 就是所謂的“事件環(huán)”。

明白了javascript的執(zhí)行原理,我們就不難理解之前的第二段代碼,為什么setTimeout為0時會最后執(zhí)行,因為setTimeout是異步代碼,必須要等所有的同步代碼都執(zhí)行完,才會執(zhí)行異步隊列。即使setTimeout執(zhí)行得再快,它也不可能在同步代碼之前執(zhí)行。

瀏覽器中的事件環(huán)

聊了這么多,我們好像還沒有說宏任務(wù)和微任務(wù)的話題呢,上面說了,異步任務(wù)又分為微任務(wù)和宏任務(wù),那它們又是一個怎樣的執(zhí)行機制呢?

注意!微任務(wù)和宏任務(wù)的執(zhí)行方式在瀏覽器和Node中有差異,有差異!重要的事我們多說幾遍,以下我們討論的是在瀏覽器的環(huán)境里。

在瀏覽器的執(zhí)行環(huán)境中,總是先執(zhí)行小的、微任務(wù),再執(zhí)行大的、宏任務(wù),回過頭再看看第三段代碼,為什么Promise的then方法在setTimeout之前執(zhí)行?其根本原理就是因為Promise的then方法是一個微任務(wù),而setTimeout是一個宏任務(wù)。

接下來我們借用阮一峰老師的一張圖來說明:

其實,以上這張圖示我們可以再將它細化一點,這個圖上的異步隊列只畫了一個,也就是說沒有區(qū)分微任務(wù)隊列和宏任務(wù)隊列。我們可以腦補一下,在此圖上多加一個微任務(wù)隊列,當(dāng)javascript執(zhí)行時再多加一個判斷,如果是微任務(wù)就加到微任務(wù)隊列里,宏任務(wù)就加到宏任務(wù)隊列里,在清空隊列時,瀏覽器總會優(yōu)先清空“微任務(wù)”。這樣就把瀏覽器的事件環(huán)撤底說全了。

最后來一個大考,以下代碼的運行結(jié)果是什么:

將此代碼拷到chrome中跑一下,結(jié)果是:

5
4
1
2
3

不妨我們試著分析一下為什么是這個結(jié)果,首先輸出5,因為console.log(5)是同步代碼,這沒什么可說的。

之后將前兩個setTimeout和最后一個Promise放入異步隊列,注意它們的區(qū)分,此時執(zhí)行完了同步代碼之后發(fā)現(xiàn)微任務(wù)和宏任務(wù)隊列中都有代碼,按瀏覽器的事件環(huán)機制,優(yōu)先執(zhí)行微任務(wù),此時輸出4。

然后執(zhí)行宏任務(wù)隊列里的第一個setTimeout,輸出1。

此時,setTimeout中又有一個Promise,放入微任務(wù)隊列。

再次清空微任務(wù)隊列,輸出2。

最后宏任務(wù)隊列里還有最后一個setTimeout,輸出3。

Node中的事件環(huán)

而Node中的事件環(huán)又和瀏覽器有些許的不同,在node.js的官方文檔中有專門的描述,其中文檔中有一張圖,詳細的說明了它的事件環(huán)機制,我們把它拿出來:

可以看到,node.js中的事件環(huán)機制分為了6個階段,其中最重要的3個階段我在上面做了注明:

timer階段,指的就是setTimeout等宏任務(wù)

poll輪詢階段,如讀取文件等宏任務(wù)

check階段,setImmediate宏任務(wù)

圖中每一個階段都代表了一個宏任務(wù)隊列,在Node事件環(huán)中,微任務(wù)的運行時機是在每一個“宏任務(wù)隊列”清空之后,在進入下一個宏任務(wù)隊列之間執(zhí)行。這是和瀏覽器的最大區(qū)別。

還是用代碼說話吧,有一道經(jīng)典的Node.js事件環(huán)面試題:

const fs = require("fs");

fs.readFile("./1.txt", (err, data) => {
    setTimeout(() => {
        console.log("timeout");
    });
    setImmediate(() => {
        console.log("immediate");
    });
    Promise.resolve().then(() => {
        console.log("Promise");
    });
});

運行結(jié)果:

Promise
immediate
timeout

代碼并不復(fù)雜,首先使用fs模塊讀取了一個文件,在回調(diào)的內(nèi)部有兩個宏任務(wù)和一個微任務(wù),微任務(wù)總是優(yōu)于宏任務(wù)執(zhí)行的,因此先輸出Promise。

但是之后的區(qū)別為什么先輸出immdiate?原因就在于fs讀取文件的宏任務(wù)在上圖中的第4個輪詢階段,當(dāng)?shù)?個階段清空隊列之后,就該進入第5個check階段,也就是setImmediate這個宏任務(wù)所在的階段,而不會跳回第1個階段,因此先輸出immedate。

尾巴

最后總結(jié)一下,分析完瀏覽器和Node的事件環(huán)發(fā)現(xiàn)它們并不簡單,但只要記住了它們之間的區(qū)別就可以分析出結(jié)果。

瀏覽器事件環(huán)是運行完一個宏任務(wù)馬上清空微任務(wù)隊列
Node事件環(huán)是清空完一個階段的宏任務(wù)隊列之后再清空微任務(wù)隊列

最后,總結(jié)一下常見的宏任務(wù)和微任務(wù):

宏任務(wù) 微任務(wù)
setTimeout Promise的then方法
setInterval process.nextTick
setImmediate MutationObserver
MessageChannel

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/97544.html

相關(guān)文章

  • 設(shè)計模式系列·Facade模式之MVC煩惱

    摘要:沒有任何意外,王小二的公司用來開發(fā)公司的主打產(chǎn)品。臃腫的著手開干吧小二打開熟悉的,找到提交訂單模塊的。要不再去請教下哥的煩惱小二找到哥,詳細的描述了他的問題。 流行的MVC架構(gòu)模式 如今的Web開發(fā),各種框架風(fēng)起云涌,勢如破竹。 從國民第一的ThinkPhp到稱霸全球的Laravel,這些框架有一個共同特征,都采用了MVC的架構(gòu)模式。 showImg(https://segmentfa...

    zhichangterry 評論0 收藏0
  • 編程范式 —— 函數(shù)式編程入門

    摘要:在函數(shù)式編程中數(shù)據(jù)在由純函數(shù)組成的管道中傳遞。函數(shù)式編程中函子是實現(xiàn)了函數(shù)的容器下文中將函子視為范疇,模型可表示如下但是在函數(shù)式編程中要避免使用這種面向?qū)ο蟮木幊谭绞饺《畬ν獗┞读艘粋€的接口也稱為。 showImg(https://segmentfault.com/img/remote/1460000018101204); 該系列會有 3 篇文章,分別介紹什么是函數(shù)式編程、剖析函數(shù)...

    flyer_dev 評論0 收藏0
  • 使用fjpublish發(fā)布前端項目(基礎(chǔ)篇)

    摘要:任務(wù)描述使用的來發(fā)布多個目錄使用的忽略所有結(jié)尾的文件。任務(wù)描述使用的配置項在項目發(fā)布后重啟進程忽略當(dāng)次構(gòu)建過程并提交一次使用的配置項設(shè)置每個遠程命令超時時間為秒。下一期地址使用發(fā)布前端項目安全篇官方交流群 本系列文章共分為基礎(chǔ)篇,安全篇,拓展篇。 前言 曾幾何時,我相信部分Web Developer(包括我)使用的項目發(fā)布方式比較傳統(tǒng)(使用xftp或者sublime text的插件sf...

    anonymoussf 評論0 收藏0
  • 聊聊幽靈Class

    摘要:有鎖的地方就會有鎖競爭,并且也是一個耗時的過程,所以同一個如果并發(fā)出現(xiàn)在日志堆棧中勢必會導(dǎo)致一部分線程會,這對于線上系統(tǒng)中簡直就是災(zāi)難改進在最近的版本中并沒有發(fā)現(xiàn)對的類做特殊處理,并且正如官方說的所以只要的版本大于,這個配置默認(rèn)都是關(guān)閉的 起因 偶然一次路過同事電腦,看著黑底藍色滿屏的堆棧信息,過去笑著拍了拍他的肩膀說道「小哥,又在寫B(tài)UG呢」湊過去仔細看了一眼異常堆棧詳情,「虎軀一震...

    Ilikewhite 評論0 收藏0
  • 牛仔很忙之大話HTTPS

    摘要:移動組的壕星在灌水群里發(fā)了條消息,這家伙又準(zhǔn)備顯擺他的腎了。什么是小夏弱弱的問。配備身份證書,防止身份被冒充。那這么說,豈不是將來所有網(wǎng)站都會用小夏接著道,又加了個羞澀的表情。 中秋前,本牛仔正在苦思新任務(wù)的對策,突然桌面右下角的小企鵝一陣跳動:剛收到消息, 第三方下載的XCode被植入后門,N多app信息疑似泄密。還是你們用安卓的好啊,唉。 移動組的壕星在灌水群里發(fā)了條消息,這家伙又...

    MonoLog 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<