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