<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          JavaScript之徹底理解 EventLoop

          共 3110字,需瀏覽 7分鐘

           ·

          2021-11-01 15:01

          點(diǎn)擊上方?前端瓶子君,關(guān)注公眾號(hào)

          回復(fù)算法,加入前端編程面試算法每日一題群

          前言

          Event Loop即事件循環(huán),是瀏覽器或Node解決單線程運(yùn)行時(shí)不會(huì)阻塞的一種機(jī)制。

          在正式學(xué)習(xí)Event Loop之前,先需要解決幾個(gè)問題:

          1. 什么是同步與異步?
          2. JavaScript是一門單線程語言,那如何實(shí)現(xiàn)異步?
          3. 同步任務(wù)和異步任務(wù)的執(zhí)行順序如何?
          4. 異步任務(wù)是否存在優(yōu)先級(jí)?

          同步與異步

          計(jì)算機(jī)領(lǐng)域中的同步與異步和我們現(xiàn)實(shí)社會(huì)的同步和異步正好相反。現(xiàn)實(shí)中的同步,就是同時(shí)進(jìn)行,突出的是"同",比如看足球比賽的時(shí)候吃著零食,兩件事情同時(shí)發(fā)生;異步就是不同時(shí)。但計(jì)算機(jī)中與現(xiàn)實(shí)存在一定差異。

          舉個(gè)栗子

          天氣冷了,早上剛醒來想喝點(diǎn)熱水暖暖身子,但這每天起早貪黑996,晚上回來太累躺下就睡,沒開水啊,沒法子,只好急急忙忙去燒水。

          現(xiàn)在早上太冷了啊,不由得在被窩里面多躺了一會(huì),收拾的時(shí)間緊緊巴巴,不能空等水開,于是我便趁此去洗漱,收拾自己。洗漱完,水開了,喝到暖暖的熱水,舒服??!

          舒服完,開啟新的996之日,打工人出發(fā)!

          燒水和洗漱是在同時(shí)間進(jìn)行的,這就是計(jì)算機(jī)中的異步。

          計(jì)算機(jī)中的同步是連續(xù)性的動(dòng)作,上一步未完成前,下一步會(huì)發(fā)生堵塞,直至上一步完成后,下一步才可以繼續(xù)執(zhí)行。例如:只有等水開,才能喝到暖暖的熱水。

          單線程卻可以異步?

          JavaScript的確是一門單線程語言,但是瀏覽器UI是多線程的,異步任務(wù)借助瀏覽器的線程和JavaScript的執(zhí)行機(jī)制實(shí)現(xiàn)。例如,setTimeout就借助瀏覽器定時(shí)器觸發(fā)線程的計(jì)時(shí)功能來實(shí)現(xiàn)。

          瀏覽器線程

          1. GUI渲染線程
            • 繪制頁面,解析HTML、CSS,構(gòu)建DOM樹等
            • 頁面的重繪和重排
            • 與JS引擎互斥(JS引擎阻塞頁面刷新)
          2. JS引擎線程
            • js腳本代碼執(zhí)行
            • 負(fù)責(zé)執(zhí)行準(zhǔn)備好的事件,例如定時(shí)器計(jì)時(shí)結(jié)束或異步請(qǐng)求成功且正確返回
            • 與GUI渲染線程互斥
          3. 事件觸發(fā)線程
            • 當(dāng)對(duì)應(yīng)的事件滿足觸發(fā)條件,將事件添加到j(luò)s的任務(wù)隊(duì)列末尾
            • 多個(gè)事件加入任務(wù)隊(duì)列需要排隊(duì)等待
          4. 定時(shí)器觸發(fā)線程
            • 負(fù)責(zé)執(zhí)行異步的定時(shí)器類事件:setTimeout、setInterval等
            • 瀏覽器定時(shí)計(jì)時(shí)由該線程完成,計(jì)時(shí)完畢后將事件添加至任務(wù)隊(duì)列隊(duì)尾
          5. HTTP請(qǐng)求線程
            • 負(fù)責(zé)異步請(qǐng)求
            • 當(dāng)監(jiān)聽到異步請(qǐng)求狀態(tài)變更時(shí),如果存在回調(diào)函數(shù),該線程會(huì)將回調(diào)函數(shù)加入到任務(wù)隊(duì)列隊(duì)尾

          同步與異步執(zhí)行順序

          1. JavaScript將任務(wù)分為同步任務(wù)和異步任務(wù),同步任務(wù)進(jìn)入主線中中,異步任務(wù)首先到Event Table進(jìn)行回調(diào)函數(shù)注冊(cè)。
          2. 當(dāng)異步任務(wù)的觸發(fā)條件滿足,將回調(diào)函數(shù)從Event Table壓入Event Queue中。
          3. 主線程里面的同步任務(wù)執(zhí)行完畢,系統(tǒng)會(huì)去Event Queue中讀取異步的回調(diào)函數(shù)。
          4. 只要主線程空了,就會(huì)去Event Queue讀取回調(diào)函數(shù),這個(gè)過程被稱為Event Loop。

          舉個(gè)栗子

          • setTimeout(cb, 1000),當(dāng)1000ms后,就將cb壓入Event Queue。
          • ajax(請(qǐng)求條件, cb),當(dāng)http請(qǐng)求發(fā)送成功后,cb壓入Event Queue。

          EventLoop執(zhí)行流程

          Event Loop執(zhí)行的流程如下:

          下面一起來看一個(gè)例子,熟悉一下上述流程。

          //?下面代碼的打印結(jié)果?
          //?同步任務(wù)?打印?first
          console.log("first");?????
          setTimeout(()?=>?{????????
          //?異步任務(wù)?壓入Event?Table?4ms之后cb壓入Event?Queue
          ??console.log("second");
          },0)
          //?同步任務(wù)?打印last
          console.log("last");?????
          //?讀取Event?Queue?打印second
          復(fù)制代碼

          常見異步任務(wù)

          • DOM事件
          • AJAX請(qǐng)求
          • 定時(shí)器setTimeoutsetlnterval
          • ES6Promise

          異步任務(wù)的優(yōu)先級(jí)

          下面繼續(xù)來看一個(gè)案例:

          setTimeout(()?=>?{
          ??console.log(1);
          },?1000)
          new?Promise(function(resolve){
          ????console.log(2);
          ????for(var?i?=?0;?i?10000;?i++){
          ????????i?==?99?&&?resolve();
          ????}
          }).then(function(){
          ????console.log(3)
          });
          console.log(4)
          復(fù)制代碼

          按照上面的學(xué)習(xí):可以很輕松得出案例的打印結(jié)果:2,4,1,3。

          Promise定義部分為同步任務(wù),回調(diào)部分為異步任務(wù)

          將案例代碼在控制臺(tái)運(yùn)行,最終返回結(jié)果卻有些出人意料:

          在這里插入圖片描述

          剛看到如此結(jié)果,我的第一感覺是,setTimeout函數(shù)1s觸發(fā)太慢導(dǎo)致它加入Event Queue的時(shí)間晚于Promise.then

          于是我修改了setTimeout的回調(diào)時(shí)間為0(瀏覽器最小觸發(fā)時(shí)間為4ms),但結(jié)果仍為發(fā)生改變。

          那么也就意味著,JavaScript的異步任務(wù)是存在優(yōu)先級(jí)的。

          宏任務(wù)和微任務(wù)

          JavaScript除了廣義上將任務(wù)劃分為同步任務(wù)和異步任務(wù),還對(duì)異步任務(wù)進(jìn)行了更精細(xì)的劃分。異步任務(wù)又進(jìn)一步分為微任務(wù)和宏任務(wù)。

          在這里插入圖片描述
          • history traversal任務(wù)(h5當(dāng)中的歷史操作)
          • process.nextTicknodejs中的一個(gè)異步操作)
          • MutationObserverh5里面增加的,用來監(jiān)聽DOM節(jié)點(diǎn)變化的)

          宏任務(wù)和微任務(wù)分別有各自的任務(wù)隊(duì)列Event Queue,即宏任務(wù)隊(duì)列和微任務(wù)隊(duì)列。

          Event Loop執(zhí)行過程

          了解到宏任務(wù)與微任務(wù)過后,我們來學(xué)習(xí)宏任務(wù)與微任務(wù)的執(zhí)行順序。

          1. 代碼開始執(zhí)行,創(chuàng)建一個(gè)全局調(diào)用棧,script作為宏任務(wù)執(zhí)行
          2. 執(zhí)行過程過同步任務(wù)立即執(zhí)行,異步任務(wù)根據(jù)異步任務(wù)類型分別注冊(cè)到微任務(wù)隊(duì)列和宏任務(wù)隊(duì)列
          3. 同步任務(wù)執(zhí)行完畢,查看微任務(wù)隊(duì)列
            • 若存在微任務(wù),將微任務(wù)隊(duì)列全部執(zhí)行(包括執(zhí)行微任務(wù)過程中產(chǎn)生的新微任務(wù))
            • 若無微任務(wù),查看宏任務(wù)隊(duì)列,執(zhí)行第一個(gè)宏任務(wù),宏任務(wù)執(zhí)行完畢,查看微任務(wù)隊(duì)列,重復(fù)上述操作,直至宏任務(wù)隊(duì)列為空

          更新一下Event Loop的執(zhí)行順序圖:

          在這里插入圖片描述

          總結(jié)

          在上面學(xué)習(xí)的基礎(chǔ)上,重新分析當(dāng)前案例:

          setTimeout(()?=>?{
          ??console.log(1);
          },?1000)
          new?Promise(function(resolve){
          ????console.log(2);
          ????for(var?i?=?0;?i?10000;?i++){
          ????????i?==?99?&&?resolve();
          ????}
          }).then(function(){
          ????console.log(3)
          });
          console.log(4)
          復(fù)制代碼

          分析過程見下圖:

          面試題

          文章的最后附贈(zèng)幾道經(jīng)典面試題,可以測(cè)試一下自己對(duì)Event Loop的掌握程度。

          題目一

          console.log('script?start');

          setTimeout(()?=>?{
          ????console.log('time1');
          },?1?*?2000);

          Promise.resolve()
          .then(function()?{
          ????console.log('promise1');
          }).then(function()?{
          ????console.log('promise2');
          });


          async?function?foo()?{
          ????await?bar()
          ????console.log('async1?end')
          }
          foo()

          async?function?errorFunc?()?{
          ????try?{
          ????????await?Promise.reject('error!!!')
          ????}?catch(e)?{
          ????????console.log(e)
          ????}
          ????console.log('async1');
          ????return?Promise.resolve('async1?success')
          }
          errorFunc().then(res?=>?console.log(res))

          function?bar()?{
          ????console.log('async2?end')?
          }

          console.log('script?end');
          復(fù)制代碼

          題目二

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

          const?P?=?new?Promise((resolve,?reject)?=>?{
          ????console.log(2)
          ????setTimeout(()?=>?{
          ????????resolve()
          ????????console.log(3)
          ????},?0)
          })

          P.then(()?=>?{
          ????console.log(4)
          })
          console.log(5)
          復(fù)制代碼

          題目三

          var?p1?=?new?Promise(function(resolve,?reject){
          ????resolve("2")
          })

          setTimeout(function(){
          ????console.log("1")
          },10)

          p1.then(function(value){
          ????console.log(value)
          })

          setTimeout(function(){
          ????console.log("3")
          },0)
          復(fù)制代碼


          關(guān)于本文

          來源:戰(zhàn)場(chǎng)小包

          https://juejin.cn/post/7020328988715270157


          最后

          歡迎關(guān)注【前端瓶子君】??ヽ(°▽°)ノ?
          回復(fù)「算法」,加入前端編程源碼算法群,每日一道面試題(工作日),第二天瓶子君都會(huì)很認(rèn)真的解答喲!
          回復(fù)「交流」,吹吹水、聊聊技術(shù)、吐吐槽!
          回復(fù)「閱讀」,每日刷刷高質(zhì)量好文!
          如果這篇文章對(duì)你有幫助,在看」是最大的支持
          ?》》面試官也在看的算法資料《《
          “在看和轉(zhuǎn)發(fā)”就是最大的支持


          瀏覽 59
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产精品久久久久久爽爽爽麻豆色哟哟 | 一区二区三区未删减 | 中国一区二区在线观看 | 丁香激情国产色五月 | 欧美成年性精品三级网站 |