91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

javascript順序執行是怎樣的

發布時間:2022-02-09 15:14:04 來源:億速云 閱讀:178 作者:iii 欄目:web開發

這篇文章主要講解了“javascript順序執行是怎樣的”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“javascript順序執行是怎樣的”吧!

javascript是順序執行。JavaScript是單線程語言,執行順序是自上而下的,也就是說代碼在執行過程中,另一段代碼想要執行就必須等當前代碼執行完成后才可以進行。

本教程操作環境:windows7系統、javascript1.8.5版、Dell G3電腦。

JavaScript 的順序執行 執行機制

1.單線程的JavaScript

大家都知道,JavaScript是單線程語言,執行順序是自上而下。JavaScript沒有多線程的概念,所有的程序都是單線程依次執行的。就是代碼在執行過程中,另一段代碼想要執行就必須等當前代碼執行完成后才可以進行。

注意,JavaScript 只在一個線程上運行,不代表 JavaScript 引擎只有一個線程。事實上,JavaScript 引擎有多個線程,單個腳本只能在一個線程上運行(稱為主線程),其他線程都是在后臺配合

那為什么js是單線程而不能是多線程呢?多線程效率不是更高嗎?

JavaScript 之所以采用單線程,而不是多線程,與它的用途有關系,作為網頁腳本語言,JavaScript的主要用途是與用戶互動,以及操作DOM。

如果 JavaScript 同時有兩個線程,一個線程在網頁 DOM 節點上添加內容,另一個線程刪除了這個節點,這時瀏覽器應該以哪個線程為準?是不是還要有鎖機制?

JavaScript 從誕生起就是單線程,原因是不想讓瀏覽器變得太復雜,因為多線程需要共享資源、且有可能修改彼此的運行結果,對于一種網頁腳本語言來說,這就太復雜了。

所以,為了避免復雜性,JavaScript 一開始就是單線程,這已經成了這門語言的核心特征,將來也不會改變。

這種模式的好處是實現起來比較簡單,執行環境相對單純;壞處是只要有一個任務耗時很長,后面的任務都必須排隊等著,會拖延整個程序的執行。

常見的瀏覽器無響應(假死),往往就是因為某一段 JavaScript 代碼長時間運行(比如死循環),導致整個頁面卡在這個地方,其他任務無法執行。

JavaScript 語言本身并不慢,慢的是讀寫外部數據,比如等待 Ajax 請求返回結果。這個時候,如果對方服務器遲遲沒有響應,或者網絡不通暢,就會導致腳本的長時間停滯。

如果排隊是因為計算量大,CPU 忙不過來,倒也算了,但是很多時候 CPU 是閑著的,因為 IO 操作(輸入輸出)很慢(比如 Ajax 操作從網絡讀取數據),不得不等著結果出來,再往下執行。

JavaScript 語言的設計者意識到,這時 CPU 完全可以不管 IO 操作,掛起處于等待中的任務,先運行排在后面的任務。等到 IO 操作返回了結果,再回過頭,把掛起的任務繼續執行下去。這種機制就是 JavaScript 內部采用的“事件循環”機制(Event Loop)。

單線程模型雖然對 JavaScript 構成了很大的限制,但也因此使它具備了其他語言不具備的優勢。如果用得好,JavaScript 程序是不會出現堵塞的,這就是為什么 Node 可以用很少的資源,應付大流量訪問的原因。

為了利用多核 CPU 的計算能力,HTML5 提出 Web Worker 標準,允許 JavaScript 腳本創建多個線程,但是子線程完全受主線程控制,且不得操作 DOM。所以,這個新標準并沒有改變 JavaScript 單線程的本質。

現在我們看一段代碼

function fn(){
    console.log('start');
    setTimeout(()=>{
        console.log('setTimeout');
    },0);
    console.log('end');
}

fn() // 輸出 start end setTimeout

既然JavaScript的執行順序是自上而下的,那上面那段代碼的執行順序為什么會被打亂了呢?

因為JavaScript的執行模式有兩種:同步 和 異步

2.JavaScript的同步和異步

程序里面所有的任務,可以分成兩類:同步任務(synchronous)異步任務(asynchronous)
什么是同步和異步呢?同步和異步又是如何實現的呢?

同步任務 :是那些沒有被引擎掛起、在主線程上排隊執行的任務。只有前一個任務執行完畢,才能執行后一個任務。

異步任務: 是那些被引擎放在一邊,不進入主線程、而進入 任務隊列 的任務。只有引擎認為某個異步任務可以執行了(比如 Ajax 操作從服務器得到了結果),該任務(采用回調函數的形式)才會進入主線程執行。(通俗講就是 只有"任務隊列"通知主線程,某個異步任務可以執行了,該任務才會進入主線程執行。)

排在異步任務后面的代碼,不用等待異步任務結束會馬上運行,也就是說,異步任務不具有“堵塞”效應。

舉例來說,Ajax 操作可以當作同步任務處理,也可以當作異步任務處理,由開發者決定。如果是同步任務,主線程就等著 Ajax 操作返回結果,再往下執行;如果是異步任務,主線程在發出 Ajax 請求以后,就直接往下執行,等到 Ajax 操作有了結果,主線程再執行對應的回調函數。

js中包含諸多創建異步的函數如:
seTimeout,setInterval,dom事件,ajax,Promise,process.nextTick等函數

3.任務隊列和事件循環

JavaScript 運行時,除了一個正在運行的主線程,引擎還提供一個任務隊列(task queue),里面是各種需要當前程序處理的異步任務。(實際上,根據異步任務的類型,存在多個任務隊列。為了方便理解,這里假設只存在一個隊列。)

javascript順序執行是怎樣的

首先,主線程會去執行所有的同步任務。等到同步任務全部執行完,就會去看任務隊列里面的異步任務。
如果滿足條件,那么異步任務就重新進入主線程開始執行,這時它就變成同步任務了。等到執行完,下一個異步任務再進入主線程開始執行。一旦任務隊列清空,程序就結束執行。

異步任務的寫法通常是回調函數。一旦異步任務重新進入主線程,就會執行對應的回調函數。如果一個異步任務沒有回調函數,就不會進入任務隊列,也就是說,不會重新進入主線程,因為沒有用回調函數指定下一步的操作。

javascript順序執行是怎樣的

  • 因為單線程,所以代碼自上而下執行,所有代碼被放到執行棧中執行;

  • 遇到異步函數將回調函數添加到一個任務隊列里面;

  • 執行棧中的代碼執行完以后,會去循環任務隊列里的函數;

  • 任務隊列里的函數放到執行棧中執行;

  • 如此往復,稱為事件循環;

JavaScript 引擎怎么知道異步任務有沒有結果,能不能進入主線程呢?答案就是引擎在不停地檢查,一遍又一遍,只要同步任務執行完了,引擎就會去檢查那些掛起來的異步任務,是不是可以進入主線程了。這種循環檢查的機制,就叫做事件循環。
這樣分析,上面那一段代碼就得到了合理的解釋。
再來看一下這段代碼:

function fn() {
    setTimeout(()=>{
        console.log('a');
    },0);
    new Promise((resolve)=>{
        console.log('b');
        resolve();
    }).then(()=>{
        console.log('c')
    });
}
fn() // b c a

4.Promise和async 立即執行

Promise中的異步體現在then和catch中,所以寫在Promise中的代碼是被當做同步任務立即執行的。
而在async/await中,在出現await出現之前,其中的代碼也是立即執行的。那么出現了await時候發生了什么呢?

await等到之后做了什么?

很多人以為await會一直等待之后的表達式執行完之后才會繼續執行后面的代碼,實際上await是一個讓出線程的標志。await后面的表達式會先執行一遍,將await后面的代碼加入到微任務(microtask)中,然后就會跳出整個async函數來執行后面的代碼。

不管await后面的代碼是同步還是異步,await總是需要時間,從右向左執行,先執行右側的代碼,執行完后,發現有await關鍵字,于是讓出線程,阻塞代碼。

由于因為async await 本身就是promise+generator的語法糖。所以await后面的代碼是microtask。
例如:

async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}

等同于

async function async1() {
    console.log('async1 start');
    Promise.resolve(async2()).then(() => {
                console.log('async1 end');
        })
}

5.宏任務和微任務

兩個任務分別處于任務隊列中的宏隊列 和 微隊列中;
宏任務隊列微任務隊列組成了任務隊列;
任務隊列將任務放入執行棧中執行

宏任務

宏隊列,macrotask,也叫tasks。
異步任務的回調會依次進入macro task queue,等待后續被調用。

宏任務一般包括:

  1. 整體代碼script

  2. setTimeout

  3. setInterval

  4. setImmediate (Node獨有)

  5. requestAnimationFrame (瀏覽器獨有)

  6. I/O

  7. UI rendering (瀏覽器獨有)

微任務

微隊列,microtask,也叫jobs。
異步任務的回調會依次進入micro task queue,等待后續被調用

微任務一般包括:

  • process.nextTick (Node獨有)

  • Promise

  • Object.observe

  • MutationObserver

javascript順序執行是怎樣的

javascript順序執行是怎樣的

1.執行全局Script同步代碼,這些同步代碼有一些是同步語句,有一些是異步語句(比如setTimeout等)。
2.全局Script代碼執行完畢后,執行棧Stack會清空。
3.先從微任務隊列中取出位于隊首的回調任務,放入執行棧Stack中執行,執行完后微隊列長度減1。
4.繼續循環取出位于微隊列的任務,放入執行棧Stack中執行,以此類推,直到直到把微任務執行完畢。注意,如果在執行微任務的過程中,又產生了微任務,那么會加入到微隊列的末尾,也會在這個周期被調用執行。
5.微隊列中的所有微任務都執行完畢,此時微隊列為空隊列,執行棧Stack也為空。
6.取出宏隊列中的任務,放入執行棧Stack中執行。
7.執行完畢后,執行棧Stack為空。
8.重復第3-7個步驟。

以上是完成的****事件循環

6.面試題測試

現在我們再來分析一下最開始的那個面試題

async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
async function async2() {
    console.log('async2');
}

console.log('script start');

setTimeout(function() {
    console.log('setTimeout');
}, 0)

async1();

new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(function() {
    console.log('promise2');
});
console.log('script end');


/*
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
*/

我們分析一下整個過程:

1.首先,事件循環從宏任務(macrotask)隊列開始,這個時候,宏任務隊列中,只有一個script(整體代碼)任務;當遇到任務源(task source)時,則會先分發任務到對應的任務隊列中去。

2.然后我們看到首先定義了兩個async函數,接著往下看,然后遇到了 console 語句,直接輸出 script start。輸出之后,script 任務繼續往下執行,遇到 setTimeout,其作為一個宏任務源,則會先將其任務分發到對應的隊列中。

3.script 任務繼續往下執行,執行了async1()函數,前面講過async函數中在await之前的代碼是立即執行的,所以會立即輸出async1 start。
遇到了await時,會將await后面的表達式執行一遍,所以就緊接著輸出async2,然后將await后面的代碼也就是console.log(‘async1 end’)加入到microtask中的Promise隊列中,接著跳出async1函數來執行后面的代碼。

4.script任務繼續往下執行,遇到Promise實例。由于Promise中的函數是立即執行的,而后續的 .then 則會被分發到 microtask 的 Promise 隊列中去。所以會先輸出 promise1,然后執行 resolve,將 promise2 分配到對應隊列。

5.script任務繼續往下執行,最后只有一句輸出了 script end,至此,全局任務就執行完畢了。
根據上述,每次執行完一個宏任務之后,會去檢查是否存在 Microtasks;如果有,則執行 Microtasks 直至清空 Microtask Queue。
因而在script任務執行完畢之后,開始查找清空微任務隊列。此時,微任務中, Promise 隊列有的兩個任務async1 end和promise2,因此按先后順序輸出 async1 end,promise2。當所有的 Microtasks 執行完畢之后,表示第一輪的循環就結束了。

6.第二輪循環依舊從宏任務隊列開始。此時宏任務中只有一個 setTimeout,取出直接輸出即可,至此整個流程結束。

再來一個稍微復雜點的代碼

function fn(){
    console.log(1);
    
    setTimeout(() => {
        console.log(2);
        Promise.resolve().then(() => {
            console.log(3);
        });
    },0);
    
    new Promise((resolve, reject) => {
        console.log(4);
        resolve(5);
    }).then(data => {
        console.log(data);
    });
    
    setTimeout(() => {
        console.log(6);
    },0);
    
    console.log(7);
}
fn(); //

流程重現

1.執行函數同步語句

  • step1

console.log(1);

執行棧: [ console ]
宏任務: []
微任務: []

打印結果:
1

  • step2

setTimeout(() => {
    // 這個回調函數叫做callback1,setTimeout屬于宏任務,所以放到宏隊列中
    console.log(2);
    Promise.resolve().then(() => {
        console.log(3)
    });
});

執行棧: [ setTimeout ]
宏任務: [ callback1 ]
微任務: []

打印結果:
1

  • step3

new Promise((resolve, reject) => {
    // 注意,這里是同步執行的
    console.log(4);
    resolve(5)
}).then((data) => {
    // 這個回調函數叫做callback2,promise屬于微任務,所以放到微隊列中
    console.log(data);
});

執行棧: [ promise ]
宏任務: [ callback1 ]
微任務: [ callback2 ]

打印結果:
1
4

  • step4

setTimeout(() => {
    // 這個回調函數叫做callback3,setTimeout屬于宏任務,所以放到宏隊列中
    console.log(6);
})

執行棧: [ setTimeout ]
宏任務: [ callback1 , callback3 ]
微任務: [ callback2 ]

打印結果:
1
4

  • step5

console.log(7)

執行棧: [ console ]
宏任務: [ callback1 , callback3 ]
微任務: [ callback2 ]

打印結果:
1
4
7

2.同步語句執行完畢,從微隊列中依次取出任務執行,直到微隊列為空

  • step6

console.log(data)       // 這里data是Promise的成功參數為5

執行棧: [ callback2 ]
宏任務: [ callback1 , callback3 ]
微任務: []

打印結果:
1
4
7
5

3.這里微隊列中只有一個任務,執行完后開始從宏隊列中取任務執行

  • step7

console.log(2);

執行棧: [ callback1 ]
宏任務: [ callback3 ]
微任務: []

打印結果:
1
4
7
5
2

但是執行callback1的時候遇到另一個Promise,Promise異步執行完畢以后在微隊列中又注冊了一個callback4函數

  • step8

Promise.resolve().then(() => {
    // 這個回調函數叫做callback4,promise屬于微任務,所以放到微隊列中
    console.log(3);
});

執行棧: [ Promise ]
宏任務: [ callback3 ]
微任務: [ callback4 ]

打印結果:
1
4
7
5
2

4.取出一個宏任務macrotask執行完畢,然后再去微任務隊列microtask queue中依次取出執行

  • step9

console.log(3)

執行棧: [ callback4 ]
宏任務: [ callback3 ]
微任務: []

打印結果:
1
4
7
5
2
3

5.微隊列全部執行完,再去宏隊列**中取第一個任務執行

  • step10

console.log(6)

執行棧: [ callback3 ]
宏任務: []
微任務: []

打印結果:
1
4
7
5
2
3
6

6.以上全部執行完畢,執行棧宏隊列,****微隊列**均為空
執行棧: []
宏任務: []
微任務: []

打印結果:
1
4
7
5
2
3
6

感謝各位的閱讀,以上就是“javascript順序執行是怎樣的”的內容了,經過本文的學習后,相信大家對javascript順序執行是怎樣的這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

江川县| 灵石县| 四会市| 双辽市| 西峡县| 锦州市| 三亚市| 高唐县| 乌兰浩特市| 丽水市| 杂多县| 桦川县| 长丰县| 桑植县| 若尔盖县| 石家庄市| 永善县| 乐至县| 武穴市| 新兴县| 漳平市| 额尔古纳市| 灯塔市| 牙克石市| 墨玉县| 定边县| 阳春市| 兖州市| 荔波县| 襄汾县| 广南县| 永济市| 筠连县| 西乌| 平潭县| 蒙自县| 武邑县| 东台市| 扶绥县| 武功县| 宁武县|