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