成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

深入理解JavaScript運行機制

phodal / 2203人閱讀

摘要:換句話說當一個異步過程調(diào)用發(fā)出后,調(diào)用者不會立刻得到結果,而是調(diào)用發(fā)出后,被調(diào)用者通過狀態(tài)通知或回調(diào)函數(shù)處理這個調(diào)用。

JavaScript單線程機制

JavaScript的一個語言特性(也是這門語言的核心)就是單線程。什么是單線程呢?簡單地說就是同一時間只能做一件事,當有多個任務時,只能按照一個順序一個完成了再執(zhí)行下一個

為什么JS是單線程的呢?

JS最初被設計用在瀏覽器中,作為瀏覽器腳本語言,JavaScript的主要用途是與用戶互動,以及操作DOM

如果瀏覽器中的JS是多線程的,會帶來很復雜的同步問題

比如,假定JavaScript同時有兩個線程,一個線程在某個DOM節(jié)點上添加內(nèi)容,另一個線程刪除了這個節(jié)點,這時瀏覽器應該以哪個線程為準?

所以為了避免復雜性,JavaScript從誕生起就是單線程

為了提高CPU的利用率,HTML5提出Web Worker標準,允許JavaScript腳本創(chuàng)建多個線程,但是子線程完全受主線程控制,且不得操作DOM。所以這個標準并沒有改變JavaScript單線程的本質

任務隊列

同步和異步
同步和異步關注的是消息通知機制

同步:發(fā)出調(diào)用后,沒有得到結果之前,該調(diào)用不返回,一旦調(diào)用返回,就得到返回值了。 簡而言之就是調(diào)用者主動等待這個調(diào)用的結果

異步:調(diào)用者在發(fā)出調(diào)用后這個調(diào)用就直接返回了,所以沒有返回結果。換句話說當一個異步過程調(diào)用發(fā)出后,調(diào)用者不會立刻得到結果,而是調(diào)用發(fā)出后,被調(diào)用者通過狀態(tài)、通知或回調(diào)函數(shù)處理這個調(diào)用。

阻塞和非阻塞
阻塞和非阻塞關注的是程序在等待調(diào)用結果(消息,返回值)時的狀態(tài)

阻塞調(diào)用是指調(diào)用結果返回之前,當前線程會被掛起。調(diào)用線程只有在得到結果之后才會返回

非阻塞調(diào)用指在不能立刻得到結果之前,該調(diào)用不會阻塞當前線程

單線程意味著同一時間只能進行一件事情,前面的事情結束才能執(zhí)行后面的事件.當碰到需要時間的IO事件的時候問題就來了,必須等到這些結束后才往下進行,但這時CPU是閑著的.這樣浪費了很多計算機的性能.

JavaScript語言的設計者意識到,這時主線程完全可以不管IO設備,掛起處于等待中的任務,先運行排在后面的任務。等到IO設備返回了結果,再回過頭,把掛起的任務繼續(xù)執(zhí)行下去.

于是,所有任務可以分成兩種,一種是同步任務(synchronous),另一種是異步任務(asynchronous)。同步任務指的是,在主線程上排隊執(zhí)行的任務,只有前一個任務執(zhí)行完畢,才能執(zhí)行后一個任務;異步任務指的是,不進入主線程、而進入"任務隊列"(task queue)的任務,只有"任務隊列"通知主線程,某個異步任務可以執(zhí)行了,該任務才會進入主線程執(zhí)行。
(1)所有同步任務都在主線程上執(zhí)行,形成一個執(zhí)行棧(execution context stack)
(2)主線程之外,還存在一個"任務隊列"(task queue)。只要異步任務有了運行結果,就在"任務隊列"之中放置一個事件
(3)一旦"執(zhí)行棧"中的所有同步任務執(zhí)行完畢,系統(tǒng)就會讀取"任務隊列",看看里面有哪些事件。那些對應的異步任務,于是結束等待狀態(tài),進入執(zhí)行棧,開始執(zhí)行
(4)主線程不斷重復上面的第三步

Event Loop
主線程從任務隊列中讀取事件,這個過程是循環(huán)不斷的,所以整個的這種運行機制又稱為Event Loop(事件循環(huán))

上圖中,主線程運行的時候,產(chǎn)生堆(heap)和棧(stack),堆中可存放對象, 棧中可存放變量,函數(shù),函數(shù)指針,代碼語句等

棧中的代碼調(diào)用各種外部API,它們在"任務隊列"中加入各種事件(click,load,done)
WebAPIs都是多帶帶線程,跟組件中的不一樣,不會阻塞主線程執(zhí)行,比如獲取后臺數(shù)據(jù),若同步就阻塞了,比如HTTP請求又開辟了一個線程

當執(zhí)行棧中的任務完成后,主線程會去讀取事件隊列(先進先出),執(zhí)行相應的回調(diào)函數(shù)

舉個例子,查看以下代碼

function read(){
    console.log(1);
    setTimeout(function (){
    console.log(2);
    setTimeout(function (){
    console.log(4)
    });
    });
    setTimeout(function (){
    console.log(5)
    })
    console.log(3);
}
read();
代碼執(zhí)行結果:1 3 2 5 4

先執(zhí)行同步代碼打印1,3,setTimeout異步代碼放到事件隊列中,先放的先執(zhí)行,后放的后執(zhí)行

定時器
"任務隊列"可以放置定時事件,即指定某些代碼在多少時間之后執(zhí)行

定時器功能主要由setTimeout()和setInterval()這兩個函數(shù)來完成,它們的內(nèi)部運行機制完全一樣,區(qū)別在于前者指定的代碼是一次性執(zhí)行,后者則為反復執(zhí)行,主要以setTimeout舉例說明

setTimeout()接受兩個參數(shù),第一個是回調(diào)函數(shù),第二個是推遲執(zhí)行的毫秒數(shù)

setTimeout(function () {
    console.log(3)
}, 2000);
setTimeout(function () {
    console.log(1);
    setTimeout(function () {
        console.log(2);
    }, 1000);
}, 1000);

執(zhí)行結果是:1 3 2

setTimeout()將事件放到等待任務隊里中,當主任務隊列的任務執(zhí)行完后,再執(zhí)行等待任務隊列,等待任務隊里中先返回的先執(zhí)行

setTimeout()有時候明明寫的延時3秒,實際卻5,6秒才執(zhí)行函數(shù),這是怎么回事呢?

setTimeout()只是將事件插入了“任務隊列”,必須等到當前代碼(執(zhí)行棧)執(zhí)行完,主線程才會去執(zhí)行它指定的回調(diào)函數(shù)。要是當前代碼耗時很長,有可能要等很久,所以并沒有辦法保證回調(diào)函數(shù)一定會在setTimeout()指定的時間執(zhí)行

Promise與process.nextTick(callback)

除了廣義的同步任務和異步任務,我們對任務有更精細的定義:

macro-task(宏任務):包括整體代碼script,setTimeout,setInterval

micro-task(微任務):Promise,process.nextTick

- process.nextTick:在事件循環(huán)的下一次循環(huán)中調(diào)用 callback 回調(diào)函數(shù)。效果是將一個函數(shù)推遲到代碼書寫的下一個同步方法執(zhí)行完畢時或異步方法的事件回調(diào)函數(shù)開始執(zhí)行時;與setTimeout(fn, 0) 函數(shù)的功能類似,但它的效率高多了

不同類型的任務會進入對應的Event Queue,比如 setTimeout 和 setInterval 會進入相同的Event Queue

事件循環(huán)的順序,決定js代碼的執(zhí)行順序。進入整體代碼(宏任務)后,開始第一次循環(huán)。接著執(zhí)行所有的微任務。然后再次從宏任務開始,找到其中一個任務隊列執(zhí)行完畢,再執(zhí)行所有的微任務。

事件循環(huán),宏任務,微任務的關系如下所示:

宏任務=>執(zhí)行結束=>有可執(zhí)行的微任務=>執(zhí)行所有微任務=>開始新的宏任務

宏任務=>執(zhí)行結束=>沒有可執(zhí)行的微任務=>開始新的宏任務

我們用一段代碼說明:

setTimeout(function () {
    console.log("setTimeout");
});
new Promise(function (resolve) {
    console.log("promise");
}).then(function () {
    console.log("then");
});
console.log("console");

這段代碼作為宏任務,進入主線程

先遇到 setTimeout ,那么將其回調(diào)函數(shù)注冊后分發(fā)到宏任務Event Queue

接下來遇到了 Promise , new Promise 立即執(zhí)行, then 函數(shù)分發(fā)到微任務Event Queue

遇到 console.log() ,立即執(zhí)行

-整體代碼script作為第一個宏任務執(zhí)行結束,看看有哪些微任務?我們發(fā)現(xiàn)了 then 在微任務Event Queue里面執(zhí)行

第一輪事件循環(huán)結束了,我們開始第二輪循環(huán),當然要從宏任務Event Queue開始。我們發(fā)現(xiàn)了宏任務Event Queue中 setTimeout 對應的回調(diào)函數(shù),立即執(zhí)行

結束

我們再看下一段代碼說明:

process.nextTick(function A() {
  console.log(1);
  process.nextTick(function B(){console.log(2);});
});

setTimeout(function timeout() {
  console.log("TIMEOUT FIRED");
}, 0)
以上代碼執(zhí)行結果:1 2 TIMEOUT FIRED

上面代碼中,由于process.nextTick方法指定的回調(diào)函數(shù),總是在當前"執(zhí)行棧"的尾部觸發(fā),所以不僅函數(shù)A比setTimeout指定的回調(diào)函數(shù)timeout先執(zhí)行,而且函數(shù)B也比timeout先執(zhí)行。這說明,如果有多個process.nextTick語句(不管它們是否嵌套),將全部在當前"執(zhí)行棧"執(zhí)行

我們再看下一段代碼說明:

function a() {
    setTimeout(function () {
        console.log("a2");
    }, 0);
    process.nextTick(function () {
        console.log("a1")
    });
}
function b() {
    process.nextTick(function () {
        console.log("b1");
    })
}
a();
b();

一個函數(shù)執(zhí)行會形成一個執(zhí)行棧,任務隊列里的回調(diào)函數(shù)每次只取一個,它執(zhí)行的時候會形成一個執(zhí)行棧,當你第一次運行這個腳本的時候,這個腳本的里所有的同步代碼都會在一個執(zhí)行棧里

a的執(zhí)行和b的執(zhí)行在一個執(zhí)行棧里,它們共同在第一個宏任務中

a執(zhí)行時候,會把a2放入宏任務隊列,把a1放入微任務隊列。

b執(zhí)行的時候,把b1放入微任務隊列

-------------------第一個宏任務執(zhí)行完畢-------------------------

宏任務執(zhí)行完畢后會把微任務隊列清空,也就是把a1 和b1都執(zhí)行,輸出a1和b1

-------------------第一個微任務隊列清空--------------------------

然后從宏任務隊列中取出下一個宏任務,也就是a2執(zhí)行.輸出a2

為什么一個宏任務要搭配處理一個微任務
因為這樣最合理,微任務就是在有空時需要立即執(zhí)行的任務,宏任務相比微任務可以滯后執(zhí)行。他們雖然都屬于異步任務,但是通過這種優(yōu)先級的設置達到了控制異步回調(diào)執(zhí)行順序的目的。值得注意的是:同步代碼執(zhí)行完會先清空微任務,然后取出宏任務隊列里的第一個事件對應的回調(diào)到執(zhí)行棧執(zhí)行,然后再清空一次微任務,如此循環(huán)...

通過以上三段代碼,您是否對JS的執(zhí)行順序有所了解呢

我們來分析一段較復雜的代碼,看看你是否真的掌握了js的執(zhí)行機制

console.log("main1");
setTimeout(function () {
    console.log("setTimeout");
    process.nextTick(function () {
        console.log("process.nextTick2");
    });
}, 0);
new Promise(function (resolve, reject) {
    console.log("promise");
    resolve();
}).then(function () {
    console.log("promise then");
});
process.nextTick(function () {
    console.log("process.nextTick1");
});
console.log("main2");

以上代碼的執(zhí)行結果是:main1=>promise=>main2=>process.nextTick1=>promise then=>setTimeout=>process.nextTick2

系統(tǒng)啟動執(zhí)行腳本,這個腳本就是一個宏任務,執(zhí)行代碼塊中所有的同步代碼,輸出main1

next1放入微任務,setTimeout+nextTick2(下一輪)放入宏任務隊列

promise構造函數(shù)部分是同步的,立刻執(zhí)行輸出promise,promise then放入微任務

下面同步代碼輸出main2

接下來執(zhí)行微任務輸出nextTick1,promise then

接下來執(zhí)行宏任務輸出setTimeout,將nexttick2放入微任務隊列

接下來執(zhí)行微任務nexttick2

nextTick是由node自己定義并實現(xiàn)的概念,它的回調(diào)調(diào)用入口在event loop過程中MakeCallback函數(shù)的末尾,驅動調(diào)用清空js層的queue,最后再執(zhí)行microtasks,適當處理下可能觸發(fā)的promise,明顯 process.nextTick1> promise.then

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

轉載請注明本文地址:http://systransis.cn/yun/92624.html

相關文章

  • 深入淺出JavaScript運行機制

    摘要:主線程從任務隊列中讀取事件,這個過程是循環(huán)不斷的,所以整個的這種運行機制又稱為事件循環(huán)。上面也提到,在到達指定時間時,定時器就會將相應回調(diào)函數(shù)插入任務隊列尾部。這就是定時器功能。關于定時器的重要補充定時器包括與兩個方法。 一、引子 本文介紹JavaScript運行機制,這一部分比較抽象,我們先從一道面試題入手: console.log(1); setTimeout(function()...

    mochixuan 評論0 收藏0
  • 深入淺出JavaScript運行機制

    摘要:主線程從任務隊列中讀取事件,這個過程是循環(huán)不斷的,所以整個的這種運行機制又稱為事件循環(huán)。上面也提到,在到達指定時間時,定時器就會將相應回調(diào)函數(shù)插入任務隊列尾部。這就是定時器功能。關于定時器的重要補充定時器包括與兩個方法。 一、引子 本文介紹JavaScript運行機制,這一部分比較抽象,我們先從一道面試題入手: console.log(1); setTimeout(function()...

    魏明 評論0 收藏0
  • 深入淺出JavaScript運行機制

    摘要:主線程從任務隊列中讀取事件,這個過程是循環(huán)不斷的,所以整個的這種運行機制又稱為事件循環(huán)。上面也提到,在到達指定時間時,定時器就會將相應回調(diào)函數(shù)插入任務隊列尾部。這就是定時器功能。關于定時器的重要補充定時器包括與兩個方法。 一、引子 本文介紹JavaScript運行機制,這一部分比較抽象,我們先從一道面試題入手: console.log(1); setTimeout(function()...

    chaosx110 評論0 收藏0
  • 【轉】深入理解JS單線程機制【原文作者:MasterYao】

    摘要:的單線程,與它的用途有關。只要指定過回調(diào)函數(shù),這些事件發(fā)生時就會進入任務隊列,等待主線程讀取。四主線程從任務隊列中讀取事件,這個過程是循環(huán)不斷的,所以整個的這種運行機制又稱為事件循環(huán)。令人困惑的是,文檔中稱,指定的回調(diào)函數(shù),總是排在前面。 原文:http://www.cnblogs.com/Master... 一、為什么JavaScript是單線程? JavaScript語言的一大特點...

    LittleLiByte 評論0 收藏0
  • 由setTimeout深入JavaScript執(zhí)行環(huán)境的異步機制

    摘要:圖片轉引自的演講和兩個定時器中回調(diào)的執(zhí)行邏輯便是典型的機制。異步編程關于異步編程我的理解是,在執(zhí)行環(huán)境所提供的異步機制之上,在應用編碼層面上實現(xiàn)整體流程控制的異步風格。 問題背景 在一次開發(fā)任務中,需要實現(xiàn)如下一個餅狀圖動畫,基于canvas進行繪圖,但由于對于JS運行環(huán)境中異步機制的不了解,所以遇到了一個棘手的問題,始終無法解決,之后在與同事交流之后才恍然大悟。問題的根節(jié)在于經(jīng)典的J...

    codeGoogle 評論0 收藏0

發(fā)表評論

0條評論

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