console.log('script start') setTimeout(function(){ console.log('setTimeout') },0) Promise.resolve() .then(function(){ console.log('promise1') }) .then(function(){ console.log('promise2') }) console.log('script end')
Running results of the above code
script start script end promise1 promise2 setTimeput
Microsoft Edge, Firefox 40, iOS Safari and desktop Safari 8.0.8setTimeout were logged before and after promise1, promise2 - although this seems to be a competitive situation. This is really strange because Firefox 39 and Safari 8.0.7 are consistently correct
Why?
Each 'thread' has its own event loop, so each web worker has its own event loop, so it can be executed independently, and all windows of the same source can share the event loop, because they can communicate synchronously, and the event loop runs continuously to perform all queued tasks. The event loop has multiple task sources, which ensure the execution order in the source (for example, indexedDB regulates their execution order), but the browser can choose which source to execute the task in each loop. This allows browsers to prioritize performance sensitive tasks, such as user input
The task is planned so that the browser can enter the javascript/DOM field from within and ensure that these operations occur in an orderly manner. Between tasks, the browser can render updates. From mouse click to event callback, you need to schedule a task, just like parsing html. In the above example, the task also needs to schedule setTimeout
SetTimeout waits for a given delay and then schedules a new task for its callback. This is why setTimeout is performed after script end, because log script end is part of the first task, and setTimeout is recorded in a separate task
Microtasks usually schedule things that should happen after the current script is executed, such as reacting to batch actions, or making some asynchronous without taking a new task,. As long as there is no other JavaScript in the middle of execution, the micro task queue will be processed after the callback and at the end of each task. Any other micro tasks queued during the micro task are added to the end of the queue and processed. The micro task includes the mutation observer callback and Promise callback as shown in the above example
Once promise is solved or promise has been solved, he will discharge the micro task into its reactionary callback. This ensures that the promise callback is asynchronous even if the promise has been resolved. Therefore then(yey, nay) calling the resolved promise will immediately queue the micro task. This is why promsie1 and promis2 are after script end, because the currently running script must be completed before the micro task. promise1 and promsie2 record setTimeout before, because micro tasks always occur before the next task
View event loops, animated presentations
<div class="outer"> <div class="inner"></div> </div> // Let's get hold of those elements var outer = document.querySelector('.outer'); var inner = document.querySelector('.inner'); // Let's listen for attribute changes on the // outer element new MutationObserver(function () { console.log('mutate'); }).observe(outer, { attributes: true, }); // Here's a click listener... function onClick() { console.log('click'); setTimeout(function () { console.log('timeout'); }, 0); Promise.resolve().then(function () { console.log('promise'); }); outer.setAttribute('data-random', Math.random()); } // ...which we'll attach to both elements inner.addEventListener('click', onClick); outer.addEventListener('click', onClick); Click inside click promise mutate click promise mutate timeout timeout external click promise mutate timeout