<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
非同步函數也是有執行順序的。本質上來說,JavaScript是單執行緒語言,不管是在瀏覽器中還是nodejs環境下。瀏覽器在執行js程式碼和渲染DOM節點都是在同一個執行緒中,執行js程式碼就無法渲染DOM,渲染DOM的時候就無法執行js程式碼。如果按照這種同步方式執行,頁面的渲染將會出現白屏甚至是報錯,特別是遇到一些耗時比較長的網路請求或者js程式碼,因此在實際開發中一般是通過非同步的方式解決。
什麼是非同步?js是一步一步執行程式碼的,遇到alert這種阻塞程式碼時,js將會停止往下執行直到阻塞程式碼執行完畢。非同步就是將函數放在單獨的非同步佇列中,不會產生阻塞,js可以繼續往下執行,等到同步程式碼執行完畢後再執行非同步佇列中的函數。因此,js會先執行完同步程式碼,才會執行非同步程式碼。非同步函數之間,雖然都是非同步,但是還是有相對的執行順序。
非同步函數的執行主要依靠事件迴圈來處理,本文重點探討非同步的分類(宏任務、微任務)、事件迴圈以及非同步函數的執行順序。
宏任務,也可簡單的說成是任務,在下一輪DOM渲染之後執行。常見的宏任務有:
setTimeout:設定一個定時器,該定時器會在設定的延遲時間到期後執行一個函數或者指定的程式碼塊。值得注意的是,setTimeout不一定會在延遲時間到達後就立即執行函數,而是會判斷執行佇列中是否還有函數沒有處理,如果沒有了並且棧為空,setTimeout才會在延遲時間到達後執行函數。
// setTimeout 延遲執行不等於到期時立即執行 let now = new Date().getSeconds(); setTimeout(() => { console.log('this is setTimeout 0'); }, 0); setTimeout(() => { console.log('this is setTimeout 200'); }, 200); while(true) { if (new Date().getSeconds() - now >= 2) { console.log('break out while loop'); break; } }
執行結果
break out while loop
this is setTimeout 0
this is setTimeout 200
先執行同步程式碼,再執行非同步。setTimeout(() => {}, 0)表示0毫秒後立即執行函數,但是當前執行佇列中還有未處理完的while迴圈,因此需要等到while迴圈執行完畢後,才會根據延遲到期時間執行函數。
setInterval:設定定時器,表示在固定的時間間隔內,重複執行某一函數或者特定的程式碼塊。注意使用setInterval有最小延遲時間限制以及確保執行時間要小於間隔時間,如果執行時間無法確定,則應採用遞迴呼叫setTimeout的方式代替。
網路請求:只要是指XMLHttpRequest等網路請求
微任務,在下一輪DOM渲染之前執行,微任務比宏任務更早執。常見的微任務有:
console.log('start'); // 微任務佇列 Promise.resolve().then(() => { console.log('promise then'); }); queueMicrotask(() => { console.log('queueMicrotask'); }); console.log('end');
執行結果
start
end
promise then
queueMicrotask
因為有非同步操作的存在,所以出現了事件迴圈,如果都是同步操作,一行一行執行程式碼,事件迴圈也就失去了用武之地。在瞭解事件迴圈前,還需要補充js的執行過程:
js在執行程式碼時,遇到函數就會將其新增到呼叫棧中,每一幀都會儲存當前函數的引數和區域性變數,當一個函數執行完畢,則會從呼叫棧中彈出,直到棧被清空,那麼程式也就執行完畢。在執行的過程中,需要的參照資料都是從堆中獲取。
在實際開發中,往往是同步程式碼和非同步程式碼都有。在js執行時,還是從第一行程式碼開始執行,遇到函數就將其新增到棧中,然後執行同步操作;如果遇到非同步函數,則根據其型別,宏任務就新增到宏任務佇列,微任務新增到微任務佇列。直到同步程式碼執行完畢,則開始執行非同步操作。
非同步操作後於同步操作,非同步操作內部也是分先後順序的。總的來說:
微任務比宏任務先執行
console.log('start'); // 宏任務佇列 setTimeout(() => { console.log('setTimeout'); }); // 微任務佇列 Promise.resolve().then(() => { console.log('promise then'); }); console.log('end'); // 執行結果 start end promise then setTimeout
微任務在下一輪DOM渲染前執行,宏任務在之後執行
let div = document.createElement('div'); div.innerHTML = 'hello world'; document.body.appendChild(div); let divList = document.getElementByTagName('div'); console.log('同步任務 length ---', list.length); console.log('start'); setTimeout(() => { console.log('setTimeout length ---', list.length); alert('宏任務 setTimeout 阻塞'); // 使用alert阻塞js執行 }); Promise.resolve().then(() => { console.log('promise then length ---', list.length); alert('微任務 promise then 阻塞); }); console.log('end');
事件迴圈
event loop會持續監聽是否有非同步操作,如果有則新增到對應的佇列中,等待執行。例如在宏任務中新增微任務,或者在微任務中新增宏任務,當前任務執行完後,可能還會有新的任務新增到事件迴圈中。
微任務中建立宏任務
new Promise((resolve) => { console.log('promise 1'); setTimeout(() => { console.log('setTimeout 1'); }, 500); resolve(); }).then(() => { console.log('promise then'); setTimeout(() => { console.log('setTimeout 2'); }, 0); }); new Promise((resolve) => { console.log('promise 2'); resolve(); })
執行結果
promise 1
promise 2
promise then
setTimeout 2
setTimeout 1
解析
js執行程式碼,遇到兩個Promise,則分別新增到微任務佇列,同步程式碼執行完畢。
在微任務佇列中根據先進先出,第一個Promise先執行,遇到setTimeout,則新增到宏任務佇列,resolve()
返回執行結果並執行then,事件迴圈將其繼續新增到微任務佇列;第一個Promise執行完畢,執行第二個Promise。
繼續執行微任務佇列,直到清空佇列。遇到setTimeout,並將其新增到宏任務佇列
宏任務佇列現在有兩個任務待執行,由於第二個setTimeout的延遲事件更小,則優先執行第二個;如果相等,則按照順序執行。
繼續執行宏任務佇列,直到清空佇列。
宏任務中建立微任務
setTimeout(() => { console.log('setTimeout 1'); new Promise((resolve) => { console.log('promise 1'); resolve(); }).then(() => { console.log('promise then'); }) }, 500); setTimeout(() => { console.log('setTimeout 2'); new Promise((resolve) => { console.log('promise 2'); resolve(); }) }, 0);
執行結果
setTimeout 2
promise 2
setTimeout 1
promise 1
promise then
解析
js執行程式碼,遇到兩個setTimeout,將其新增到宏任務佇列,同步程式碼執行完畢
先檢查微任務佇列中是否有待處理的,剛開始肯定沒有,因此直接執行宏任務佇列中的任務。第二個為零延遲,需要優先執行。遇到Promise,將其新增到微任務佇列。第一個宏任務執行完畢
在執行第二個宏任務時,微任務佇列中已經存在待處理的,因此需要先執行微任務。
微任務執行完畢,並且延遲時間到期,第一個setTimeout開始執行。遇到Promise,將其新增到微任務佇列中
執行微任務佇列中的Promise,執行完畢後遇到then,則將其繼續新增到微任務佇列
直到所有微任務執行完畢
宏任務中建立宏任務
setTimeout(() => { console.log('setTimeout 1'); setTimeout(() => { console.log('setTimeout 2'); }, 500); setTimeout(() => { console.log('setTimeout 3'); }, 500); setTimeout(() => { console.log('setTimeout 4'); }, 100); }, 0);
執行結果
setTimeout 1
setTimeout 4
setTimeout 2
setTimeout 3
解析
宏任務中建立宏任務,執行順序一般來說是按照先後順序的。對於setTImeout來說,延遲時間相同,則按照先後順序執行;延遲時間不同,則按照延遲時間的大小先後順序執行
微任務中建立微任務
new Promise((resolve) => { console.log('promise 1'); new Promise((resolve) => { console.log('promise 2'); resolve(); }); new Promise((resolve) => { console.log('promise 3'); resolve(); }) resolve(); })
執行結果
promise 1
promise 2
promise 3
解析
微任務中建立微任務,執行順序一般來說是按照先後順序執行的。
到此這篇關於JavaScript事件迴圈的文章就介紹到這了,更多相關JS事件迴圈內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45