先看一段代碼html
console.log('打印'+1); setTimeout(function(){ console.log('打印'+2); }) new Promise(function(resolve,reject){ console.log('打印'+3); }).then( console.log('打印'+4));; console.log('打印'+10); new Promise(function(resolve,reject){ setTimeout(function () { console.log('打印'+5); }); }).then( console.log('打印'+6)); setTimeout(function(){ new Promise(function(resolve,reject){ console.log('打印'+7); }); })
執行結果:前端
console.log('打印'+1); setTimeout(function(){ console.log('打印'+2); }) new Promise(function(resolve){ console.log('打印'+3); resolve(); }).then(function(){ console.log(4); } ); console.log('打印'+10); new Promise(function(resolve){ setTimeout(function () { console.log('打印'+5); }); resolve(); }).then(function(){ console.log('打印'+6)}); setTimeout(function(){ new Promise(function(resolve){ console.log('打印'+7); }); }) //執行結果: //1;3;10;4;6;2;5;7
能夠看出Promise比setTimeout()先執行。vim
由於Promise定義以後便會當即執行,其後的.then()是異步裏面的微任務。promise
而setTimeout()是異步的宏任務。瀏覽器
引自https://www.cnblogs.com/woodyblog/p/6061671.html 網絡
js是單線程語言,但js的宿主環境(好比瀏覽器,Node)是多線程的,宿主環境經過某種方式(事件驅動,下文會講)使得js具有了異步的屬性。多線程
js是單線程語言,瀏覽器只分配給js一個主線程,用來執行任務(函數),但一次只能執行一個任務,這些任務造成一個任務隊列排隊等候執行,但前端的某些任務是很是耗時的,好比網絡請求,定時器和事件監聽,若是讓他們和別的任務同樣,都老老實實的排隊等待執行的話,執行效率會很是的低,甚至致使頁面的假死。因此,瀏覽器爲這些耗時任務開闢了另外的線程,主要包括http請求線程,瀏覽器定時觸發器,瀏覽器事件觸發線程,這些任務是異步的。異步
剛纔說到瀏覽器爲網絡請求這樣的異步任務單獨開了一個線程,那麼問題來了,這些異步任務完成後,主線程怎麼知道呢?答案就是回調函數,整個程序是事件驅動的,每一個事件都會綁定相應的回調函數,舉個栗子,有段代碼設置了一個定時器函數
setTimeout(function(){ console.log(time is out); },1000);
執行這段代碼的時候,瀏覽器異步執行計時操做,當1000ms到了後,會觸發定時事件,這個時候,就會把回調函數放到任務隊列裏。整個程序就是經過這樣的一個個事件驅動起來的。
因此說,js是一直是單線程的,瀏覽器纔是實現異步的那個傢伙。oop
導圖要表達的內容用文字來表述的話:
(1)全部同步任務都在主線程上執行,造成一個執行棧(execution context stack)。
(2)主線程以外,還存在一個"任務隊列"(task queue)。只要異步任務有了運行結果,就在"任務隊列"之中放置一個事件。
(3)一旦"執行棧"中的全部同步任務執行完畢,系統就會讀取"任務隊列",看看裏面有哪些事件。那些對應的異步任務,因而結束等待狀態,進入執行棧,開始執行。
(4)主線程不斷重複上面的第三步。
只要主線程空了,就會去讀取"任務隊列",這就是JavaScript的運行機制。這個過程會不斷重複。
主線程從"任務隊列"中讀取事件,這個過程是循環不斷的,因此整個的這種運行機制又稱爲Event Loop(事件循環)。
爲了更好地理解Event Loop,請看下圖(轉引自Philip Roberts的演講《Help, I'm stuck in an event-loop》)。
上圖中,主線程運行的時候,產生堆(heap)和棧(stack),棧中的代碼調用各類外部API,它們在"任務隊列"中加入各類事件(click,load,done)。只要棧中的代碼執行完畢,主線程就會去讀取"任務隊列",依次執行那些事件所對應的回調函數。
異步任務有宏任務和微任務。
2.宏任務macrotask:
(事件隊列中的每個事件都是一個macrotask)
優先級:主代碼塊 > setImmediate > MessageChannel > setTimeout / setInterval
好比:setImmediate指定的回調函數,老是排在setTimeout前面
3.微任務包括:
優先級:process.nextTick > Promise > MutationObserver
下面這個代碼輸出結果是什麼?
主程序和和settimeout都是宏任務,兩個promise是微任務
第一個宏任務(主程序)執行完,執行所有的微任務(兩個promise),再執行下一個宏任務(settimeout),因此結果爲:
執行結果: