所有的任務(wù)分為兩種,一種是同步任務(wù),一種是異步任務(wù)。
同步任務(wù)指的是,在主線程上排隊(duì)執(zhí)行的任務(wù),只有前一個(gè)任務(wù)執(zhí)行完畢,才能執(zhí)行后一個(gè)任務(wù);
異步任務(wù)指的是,不進(jìn)入主線程、而進(jìn)入"任務(wù)隊(duì)列"(task queue)的任務(wù),只有等主線程任務(wù)執(zhí)行完畢,"任務(wù)隊(duì)列"開始通知主線程,請(qǐng)求執(zhí)行任務(wù),該任務(wù)才會(huì)進(jìn)入主線程執(zhí)行。這里說到了一個(gè)“隊(duì)列”(即任務(wù)隊(duì)列),該隊(duì)列放的是什么呢,放的就是setTimeout中的function,這些function依次加入該隊(duì)列,即該隊(duì)列中所有function中的程序?qū)?huì)在該隊(duì)列以外的所有代碼執(zhí)行完畢之后再以此執(zhí)行,這是為什么呢?因?yàn)樵趫?zhí)行程序的時(shí)候,瀏覽器會(huì)默認(rèn)setTimeout以及ajax請(qǐng)求這一類的方法都是耗時(shí)程序(盡管可能不耗時(shí)),將其加入一個(gè)隊(duì)列中,該隊(duì)列是一個(gè)存儲(chǔ)耗時(shí)程序的隊(duì)列,在所有不耗時(shí)程序執(zhí)行過后,再來依次執(zhí)行該隊(duì)列中的程序。具體來說,異步運(yùn)行機(jī)制如下:
(1)所有同步任務(wù)都在主線程上執(zhí)行,形成一個(gè)執(zhí)行棧(execution context stack)。
(2)主線程之外,還存在一個(gè)"任務(wù)隊(duì)列"(task queue)。只要異步任務(wù)有了運(yùn)行結(jié)果,就在"任務(wù)隊(duì)列"之中放置一個(gè)事件。
(3)一旦"執(zhí)行棧"中的所有同步任務(wù)執(zhí)行完畢,系統(tǒng)就會(huì)讀取"任務(wù)隊(duì)列",看看里面有哪些事件。那些對(duì)應(yīng)的異步任務(wù),于是結(jié)束等待狀態(tài),進(jìn)入執(zhí)行棧,開始執(zhí)行。
(4)主線程不斷重復(fù)上面的第三步。
setTimeOut函數(shù)為異步任務(wù),for循環(huán)為同步任務(wù),setTimeOut里的函數(shù)為回調(diào)函數(shù)。執(zhí)行順序?yàn)椋和絻?yōu)先,異步靠邊,回調(diào)墊底。所以即使setTimeOut的時(shí)間參數(shù)是0依然會(huì)放到任務(wù)隊(duì)列里,而不是主線程。主線程執(zhí)行完for循環(huán)以后才執(zhí)行異步任務(wù)setTimeOut。另外setTimeout()只是將事件插入了"任務(wù)隊(duì)列",必須等到當(dāng)前代碼(執(zhí)行棧)執(zhí)行完,主線程才會(huì)去執(zhí)行它指定的回調(diào)函數(shù)。要是當(dāng)前代碼耗時(shí)很長(zhǎng),有可能要等很久,所以并沒有辦法保證,回調(diào)函數(shù)一定會(huì)在setTimeout()指定的時(shí)間執(zhí)行。
javascript是單線程。單線程就意味著,所有任務(wù)需要排隊(duì),前一個(gè)任務(wù)結(jié)束,才會(huì)執(zhí)行后一個(gè)任務(wù)。如果前一個(gè)任務(wù)耗時(shí)很長(zhǎng),后一個(gè)任務(wù)就不得不一直等著。于是就有一個(gè)概念——任務(wù)隊(duì)列。如果排隊(duì)是因?yàn)橛?jì)算量大,CPU忙不過來,倒也算了,但是很多時(shí)候CPU是閑著的,因?yàn)镮O設(shè)備(輸入輸出設(shè)備)很慢(比如Ajax操作從網(wǎng)絡(luò)讀取數(shù)據(jù)),不得不等著結(jié)果出來,再往下執(zhí)行。于是JavaScript語言的設(shè)計(jì)者意識(shí)到,這時(shí)主線程完全可以不管IO設(shè)備,掛起處于等待中的任務(wù),先運(yùn)行排在后面的任務(wù)。等到IO設(shè)備返回了結(jié)果,再回過頭,把掛起的任務(wù)繼續(xù)執(zhí)行下去。
?
JavaScript的單線程,與它的用途有關(guān)。作為瀏覽器腳本語言,JavaScript的主要用途是與用戶互動(dòng),以及操作DOM。這決定了它只能是單線程,否則會(huì)帶來很復(fù)雜的同步問題。比如,假定JavaScript同時(shí)有兩個(gè)線程,一個(gè)線程在某個(gè)DOM節(jié)點(diǎn)上添加內(nèi)容,另一個(gè)線程刪除了這個(gè)節(jié)點(diǎn),這時(shí)瀏覽器應(yīng)該以哪個(gè)線程為準(zhǔn)?
所以,為了避免復(fù)雜性,從一誕生,JavaScript就是單線程,這已經(jīng)成這門語言的核心特征,將來也不會(huì)改變。
注:所謂單線程,是指在JS引擎中負(fù)責(zé)解釋和執(zhí)行JavaScript代碼的線程只有一個(gè)。
總結(jié):計(jì)算機(jī)中的同步就是排隊(duì)等待,假如你是第一百零一個(gè)備胎,那你只能等前面的一百個(gè)爆了之后才能‘處理'你。異步就是,盡管你是第一百零一個(gè),她還是能照顧到你的感受。
?例:
也就是說,setTimeout里的函數(shù)并沒有立即執(zhí)行,而是延遲了一段時(shí)間,滿足一定條件后,才去執(zhí)行的,這類代碼,我們叫異步代碼。
總結(jié):同步可以保證順序一致,但是容易導(dǎo)致阻塞;異步可以解決阻塞問題,但是會(huì)改變順序性,根據(jù)不同的需要去寫你的代碼。
Promise是異步的,是指他的then()和catch()方法,Promise本身還是同步的,所以這里先執(zhí)行a變量?jī)?nèi)部的Promise同步代碼。(同步優(yōu)先)
注意?new Promise()?
是同步方法,resolve
才是異步方法。?
同步(Promise)>異步(微任務(wù)(process.nextTick ,Promises.then, Promise.catch ,resove,reject,MutationObserver)>宏認(rèn)為(setTimeout,setInterval,setImmediate))
process.nextTick> Promises.then
同步代碼執(zhí)行完成后,才會(huì)再去執(zhí)行異步,哪怕異步已經(jīng)到了執(zhí)行的時(shí)間了。
[宏任務(wù):macro?task]
????????- 定時(shí)器
????????-?事件綁定
????????-?ajax
????????-?回調(diào)函數(shù)
????????-?Node中fs可以進(jìn)行異步的I/O操作
[微任務(wù):micro?task]
????????-?Promise(async/await)??=>?Promise并不是完全的同步,當(dāng)在Excutor中執(zhí)行resolve或者reject的時(shí)候,此時(shí)是異步操作,會(huì)先執(zhí)行then/catch等,當(dāng)主棧完成后,才會(huì)再去調(diào)用resolve/reject把存放的方法執(zhí)行
????????-?process.nextTick (node中實(shí)現(xiàn)的api,把當(dāng)前任務(wù)放到主棧最后執(zhí)行,當(dāng)主棧執(zhí)行完,先執(zhí)行nextTick,再到等待隊(duì)列中找)
-?MutationObserver? ?(創(chuàng)建并返回一個(gè)新的?MutationObserver?它會(huì)在指定的DOM發(fā)生變化時(shí)被調(diào)用。)
任務(wù)隊(duì)列到達(dá)時(shí)間后先進(jìn)先出的原則
?
參考:
1:https://blog.csdn.net/qq_40959677/article/details/95961443
?
聯(lián)系客服