setTimeout(function(){
console.log('1')
});
new Promise(function(resolve){
console.log('2');
resolve();
}).then(function(){
console.log('3')
});
console.log('4');
請你給出這段代碼的運行順序。
同步異步我看過很多的講解,大多都是要麼你就一個setTimeout函數,要麼就一個Promise函數。兩個函數放到一起的我還真沒見過。於是我就想:
settimeout肯定是異步的。 我也知道有一個event事件隊列,你setTimeout沒設置時間應該直接就進入這個隊列了吧,然後就是Promise的回掉函數進入event隊列。 答案應該是 2,4,1,3.
其實不然
首先說一下普通的異步函數的執行過程吧:
同步和異步任務分別進入不同的執行"場所",同步的進入主線程,異步的進入Event Table事件表並註冊函數。當指定的事情完成時,Event Table時間表會將這個函數移入事件隊列Event Queue。主線程內的任務執行完畢爲空,會去Event Queue事件隊列讀取對應的函數,進入主線程執行。上述過程會不斷重複,也就是常說的Event Loop(事件循環)。
但是js異步有一個機制,就是遇到宏任務,先執行宏任務,將宏任務放入事件隊列event queue,然後在執行微任務,將微任務放入事件隊列event queue
最騷的是,這兩個事件不放在同一個隊列 (這個queue不是一個queue)。當你往外拿的時候先從微任務事件隊列裏拿這個回調函數,然後再從宏任務的事件隊列queue上拿宏任務的回調函數。
再盜個圖
宏任務一般是:包括整體代碼script,setTimeout,setInterval。
微任務:Promise,process.nextTick。
記住就行了。然後回到開頭代碼。
因爲settimeout是宏任務,
雖然先執行的,但是他被放到了宏任務的event queue裏面,
然後代碼繼續往下檢查看有沒有微任務,檢測到Promise的then函數把他放入了微任務序列。
等到主線進程的所有代碼執行結束後。
先從微任務queue裏拿回調函數,然後微任務事件隊列queue空了後再從宏任務的事件隊列queue拿函數。
所以正確的執行結果當然是:2,4,3,1。
同步和異步的區別
同步,可以理解爲在執行完一個函數或方法之後,一直等待系統返回值或消息,這時程序是出於阻塞的,只有接收到返回的值或消息後才往下執行其他的命令。
異步,執行完函數或方法後,不必阻塞性地等待返回值或消息,只需要向系統委託一個異步過程,那麼當系統接收到返回值或消息時,系統會自動觸發委託的異步過程,從而完成一個完整的流程。
同步,就是實時處理(如打電話),比如服務器一接收客戶端請求,馬上響應,這樣客戶端可以在最短的時間內得到結果,但是如果多個客戶端,或者一個客戶端發出的請求很頻繁,服務器無法同步處理,就會造成湧塞。
同步如打電話,通信雙方不能斷(我們是同時進行,同步),你一句我一句,這樣的好處是,對方想表達的信息我馬上能收到,但是,我在打着電話,我無法做別的事情。
異步,就是分時處理(如收發短信),服務器接收到客戶端請求後並不是立即處理,而是等待服務器比較空閒的時候加以處理,可以避免湧塞。
異步如收發收短信,對比打電話,打電話我一定要在電話的旁邊聽着,保證雙方都在線,而收發短信,對方不用保證此刻我一定在手機旁,同時,我也不用時刻留意手機有沒有來短信。這樣的話,我看着視頻,然後來了短信,我就處理短信(也可以不處理),接着再看視頻。
對於寫程序,同步往往會阻塞,沒有數據過來,我就等着,異步則不會阻塞,沒數據來我幹別的事,有數據來去處理這些數據。
同步在一定程度上可以看做是單線程,這個線程請求一個方法後就待這個方法給他回覆,否則他不往下執行(死心眼)。
異步在一定程度上可以看做是多線程的(廢話,一個線程怎麼叫異步),請求一個方法後,就不管了,繼續執行其他的方法。