摘要:使用關鍵字來表示,在函數內部使用來表示異步。執行完了后,執行棧再次為空,事件觸發線程會重復上一步操作,再取出一個消息隊列中的任務,這種機制就被稱為事件循環機制。
async 函數是 Generator 函數的語法糖。使用 關鍵字 async 來表示,在函數內部使用 await 來表示異步。想較于 Generator,Async 函數的改進在于下面四點:
內置執行器 Generator 函數的執行必須依靠執行器,而 Aysnc 函數自帶執行器,調用方式跟普通函數的調用一樣
更好的語義 async 和 await 相較于 * 和 yield 更加語義化
更廣的適用性 co 模塊約定,yield 命令后面只能是Thunk 函數或 Promise對象。而 async 函數的 await 命令后面則可以是 Promise 或者原始類型的值(Number,string,boolean,但這時等同于同步操作)
返回值是 Promise async 函數返回值是 Promise 對象,比 Generator 函數返回的 Iterator 對象方便,可以直接使用 then() 方法進行調用
await命令:正常情況下,await命令后面是一個 Promise 對象,返回該對象的結果。如果不是 Promise 對象,就直接返回對應的值
下面給大家看一道之前看過的題:
function test1() { console.log("執行test1"); return "test1"; } function test2() { console.log("執行test2"); return Promise.resolve("hello test2"); } async function asyncTest() { console.log("asyncTest start..."); const v1 = await test1(); console.log(v1); const v2 = await test2(); console.log(v2); console.log(v1, v2); } setTimeout(function(){ console.log("setTimeout") },0) asyncTest(); new Promise(function(resolve){ console.log("promise1") resolve(); }).then(function(){ console.log("promise2") }) console.log("test end")
這道題結合了setTimeout、async、promise異步函數,根據三種不同異步任務執行順序可以學習js引擎的事件循環機制,咱們先看下結果:
test start... 執行test1 promise1 test end test1 執行test2 promise2 hello test2 test1,hello test2 setTimeout
再講答案之前先理解以下幾個概念:
事件循環與消息隊列
JS引擎線程遇到異步(DOM事件監聽、網絡請求、setTimeout計時器等...),會交給相應的線程多帶帶去維護異步任務,等待某個時機(計時器結束、網絡請求成功、用戶點擊DOM),然后由 事件觸發線程 將異步對應的 回調函數 加入到消息隊列中,消息隊列中的回調函數等待被執行。
同時,JS引擎線程會維護一個 執行棧,同步代碼會依次加入執行棧然后執行,結束會退出執行棧。
如果執行棧里的任務執行完成,即執行棧為空的時候(即JS引擎線程空閑),事件觸發線程才會從消息隊列取出一個任務(即異步的回調函數)放入執行棧中執行。
消息隊列是類似隊列的數據結構,遵循**先入先出(FIFO)**的規則。
執行完了后,執行棧再次為空,事件觸發線程會重復上一步操作,再取出一個消息隊列中的任務,這種機制就被稱為事件循環(event loop)機制。
主代碼塊(script)依次加入執行棧,依次執行,主代碼塊為:
setTimeout()
asyncTest()
Promise()
console.log("test end")
宏任務與微任務
macrotask(宏任務) :主代碼塊、setTimeout、setInterval等(可以看到,事件隊列中的每一個事件都是一個 macrotask,現在稱之為宏任務隊列
和 microtask(微任務):Promise、process.nextTick等
JS引擎線程首先執行主代碼塊。
每次執行棧執行的代碼就是一個宏任務,包括任務隊列(宏任務隊列)中的,因為執行棧中的宏任務執行完會去取任務隊列(宏任務隊列)中的任務加入執行棧中,即同樣是事件循環的機制。
在執行宏任務時遇到Promise等,會創建微任務(.then()里面的回調),并加入到微任務隊列隊尾。
microtask必然是在某個宏任務執行的時候創建的,而在下一個宏任務開始之前,瀏覽器會對頁面重新渲染(task >> 渲染 >> 下一個task(從任務隊列中取一個))。同時,在上一個宏任務執行完成后,渲染頁面之前,會執行當前微任務隊列中的所有微任務。
也就是說,在某一個macrotask執行完后,在重新渲染與開始下一個宏任務之前,就會將在它執行期間產生的所有microtask都執行完畢(在渲染前)。
執行機制:
執行一個宏任務(棧中沒有就從事件隊列中獲取)
執行過程中如果遇到微任務,就將它添加到微任務的任務隊列中
宏任務執行完畢后,立即執行當前微任務隊列中的所有微任務(依次執行)
當前宏任務執行完畢,開始檢查渲染,然后GUI線程接管渲染
渲染完畢后,JS引擎線程繼續,開始下一個宏任務(從宏任務隊列中獲取)
遇到異步函數 setTimeout,交給定時器觸發線程 setTimeout加入宏任務隊列,JS引擎線程繼續,出棧;
執行異步函數asyncTest,首先打印test start...
執行await test1函數首先打印"執行test1",await讓出線程去執行后面的代碼;
執行Promise 首先打印promise1,then后面函數為微任務,添加到微任務隊列中
JS引擎線程繼續向下執行同步代碼console.log("test end")打印"test end"
回到asyncTest執行await test1由于返回不是promise對象,所以直接返回test1
執行await test2()同樣先打印 "執行test2",由于test2返回promise對象 會加入到之前微任務隊列中,await繼續讓出
執行微任務隊列,由于任務隊列遵循先進先出結果,所以首先打印promise2,然后打印hello test2
微任務隊列執行完成后繼續執行asyncTest內 await之后的代碼打印 倆個await返回的值 --test1,hello test2
最后回到宏任務隊列執行setTimeout,打印setTimeout
如果我把test1變成異步函數,大家再思考一下會打印什么結果:
async function test1() { console.log("執行test1"); return "test1"; } function test2() { console.log("執行test2"); return Promise.resolve("hello test2"); } async function asyncTest() { console.log("asyncTest start..."); const v1 = await test1(); console.log(v1); const v2 = await test2(); console.log(v2); console.log(v1, v2); } setTimeout(function(){ console.log("setTimeout") },0) asyncTest(); new Promise(function(resolve){ console.log("promise1") resolve(); }).then(function(){ console.log("promise2") }) console.log("test end")
以上就是此代碼執行過程,由于本人也是在學習總結中,如有不對的地方請指教,共同學習,一起進步!!!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/99715.html
摘要:當函數結束,將會被從調用棧移出。事件循環事件循環的責任就是查看調用棧并確定調用棧是否為空。事件循環會再次檢查調用棧是否為空,如果為空的話,它會把事件回調壓入棧中,然后回調函數則被執行。 寫在文章前 這篇文章是翻譯自Sukhjinder Arora的Understanding Asynchronous JavaScript。這篇文章描述了異步和同步JavaScript是如何在運行環境中,...
摘要:現實中是這樣的執行結果為結果告訴我們,是單線程沒錯,不過不是逐行同步執行。搜索了很多官方個人博客得到了一堆詞引擎主線程事件表事件隊列宏任務微任務,徹底懵逼。。。以此規則不停的執行下去就是我們所聽到的事件循環。 都知道javascript是單線程,那么問題來了,既然是單線程順序執行,那怎么做到異步的呢? 我們理解的單線程應該是這樣的,排著一個個來,是同步執行。 showImg(https...
摘要:事件完成,回調函數進入。主線程從讀取回調函數并執行。終于執行完了,終于從進入了主線程執行。遇到,立即執行。宏任務微任務第三輪事件循環宏任務執行結束,執行兩個微任務和。事件循環事件循環是實現異步的一種方法,也是的執行機制。 本文的目的就是要保證你徹底弄懂javascript的執行機制,如果讀完本文還不懂,可以揍我。不論你是javascript新手還是老鳥,不論是面試求職,還是日常開發工作...
摘要:事件觸發線程主要負責將準備好的事件交給引擎線程執行。進程瀏覽器渲染進程瀏覽器內核,主要負責頁面的渲染執行以及事件的循環。第二輪循環結束。 將自己讀到的比較好的文章分享出來,大家互相學習,各位大佬有好的文章也可以留個鏈接互相學習,萬分感謝! 線程與進程 關于線程與進程的關系可以用下面的圖進行說明: showImg(https://segmentfault.com/img/bVbjSZt?...
閱讀 3576·2023-04-25 14:20
閱讀 1200·2021-09-10 10:51
閱讀 1157·2019-08-30 15:53
閱讀 464·2019-08-30 15:43
閱讀 2319·2019-08-30 14:13
閱讀 2797·2019-08-30 12:45
閱讀 1209·2019-08-29 16:18
閱讀 1169·2019-08-29 16:12