Event Loop
in the browser Javascript
and the Specification
PART 1
The Good, the Bad
JavaScript has a concurrency model based on an event loop*
while (queue.waitForMessage()) {
queue.processNextMessage();
}
Javascript
Implementation of ECMAScript*
ECMAScript specification is about:
* Syntactic and Lexical Grammars;
* Data Types and Values;
* Execution Contexts;
* Objects, Objects Behaviours;
* Functions and Classes;
* And etc.
has not any mention about events, timers, callbacks and etc.
ECMAScript specification
Especially it's not about Event Loop
Event Loop
is formally specified in HTML specification*
Event Loop. Processing model
WHATWG. HTML Living standard
PART 2
Event Loop
while (eventLoop.waitForTask()) {
eventLoop.processNextTask()
}
It is infinitely looping and looking for new tasks to execute.
To coordinate activities: events, user interaction, scripts, rendering, networking, and so forth, user agents
what it looks like
Event Loop
Event Loop. Task queues
An event loop has one or more task queues.
Task queue is an ordered list of tasks
while (eventLoop.waitForTask()) {
const taskQueue = eventLoop.selectTaskQueue()
if (taskQueue.hasNextTask()) {
taskQueue.processNextTask()
}
}
Task queue
- Events
- Parsing
- Callbacks
- Using a resource
- Reacting to DOM manipulation
It is an ordered list of tasks, which are algorithms that are responsible for such work as:
- The DOM manipulation
- The user interaction
- The networking
- The history traversal
Generic task sources*
Event Loop. Microtask queue
while (eventLoop.waitForTask()) {
const taskQueue = eventLoop.selectTaskQueue()
if (taskQueue.hasNextTask()) {
taskQueue.processNextTask()
}
const microtaskQueue = eventLoop.microTaskQueue
while (microtaskQueue.hasNextMicrotask()) {
microtaskQueue.processNextMicrotask()
}
}
The event loop also has a single queue called the microtask queue.
The microtask queue is completely emptied in every tick after the current task finished executing.
There are two kinds of microtasks
-
solitary callback microtasks
-
compound microtasks
Microtasks
Each event loop also has a performing a microtask checkpoint flag
JavaScript job is microtask? *
while (eventLoop.waitForTask()) {
const taskQueue = eventLoop.selectTaskQueue()
if (taskQueue.hasNextTask()) {
taskQueue.processNextTask()
}
const microtaskQueue = eventLoop.microTaskQueue
while (microtaskQueue.hasNextMicrotask()) {
microtaskQueue.processNextMicrotask()
}
if (eventLoop.shouldRender()) {
eventLoop.render()
}
}
Event Loop. Render
runTheResizeSteps()
runTheScrollSteps()
evaluateMediaQueriesAndReportChanges()
runCSSAnimationsAndSendEvents()
runTheFullscreenRenderingSteps()
runTheAnimationFrameCallbacks()
runTheUpdateIntersectionObservationsSteps()
intersectionObserverSteps()
paint()
Event Loop. Render()*
Event Loop
2. Event Loop for Browsing Context
Each worker has one event loop. Workers have restrictions *
A browsing context event loop always has at least one browsing context
There are two kinds of event loops
1. Event Loop for Worker
Browsing
Contexts
postMessage
postMessage
Web Worker
Task Queues
Task Queues
Web Worker
Task Queues
Event Loops
Event Loop. Practice
PART 3
Example of code *
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');
What this code will output to the console?
Event Loop. Example of code
Loupe
Little visualisation to help you understand how JavaScript's call stack/event loop/callback queue interact with each other.
Built by Philip Roberts. Code is on github.
PART 4
Crowd of browsers
Let's make a test*
<div class="outer">
<div class="inner" />
</div>
Test preparation. HTML part
Result in the browser
We have the following HTML code
var outer = document.querySelector('.outer');
var inner = document.querySelector('.inner');
new MutationObserver(function() {
console.log('mutate');
}).observe(outer, {
attributes: true
});
function onClick() {
console.log('click');
setTimeout(function() {
console.log('timeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise');
});
outer.setAttribute('data-random', Math.random());
}
inner.addEventListener('click', onClick);
outer.addEventListener('click', onClick);
Test preparation. Javascript part
Click the inner square
Result according Specification
click
promise
mutate
click
promise
mutate
timeout
timeout
Result of testing
click
mutate
click
mutate
promise
promise
timeout
timeout
click
promise
mutate
click
promise
mutate
timeout
timeout
click
promise
mutate
click
promise
mutate
timeout
timeout
click
click
promise
promise
mutate
timeout
timeout
Google Chrome
Mozilla Firefox
Safari
IE Edge
Result of testing *
click
mutate
click
mutate
promise
promise
timeout
timeout
click
promise
mutate
click
promise
mutate
timeout
timeout
click
promise
mutate
click
promise
mutate
timeout
timeout
click
click
promise
promise
mutate
timeout
timeout
* After article test (which posted 17 August 2015) only Safari browser behavior was successfully changed.
Mozilla Firefox was changed also, but it not correct according specification.
Google Chrome
Mozilla Firefox
Safari
IE Edge
PART 5
Long Running code
const times = 1000
const processor = new Processor();
for (let i = 1; i < times; i++) {
// calculation time, for example 10ms
processor.calculate(i);
}
console.log(
processor.result()
)
Long running code
It will lock up the Browser for 10 seconds
There's an example of long running code*
There're solutions
Make loop via setTimeout *
Loop as little as possible
const calculate = (processor, times, callback) => {
let i = 1;
const tick = () => {
if (i < times) {
processor.calculate(i);
i++;
setTimeout(tick);
} else {
callback();
}
}
setTimeout(tick);
}
const processor = new Processor();
calculate(processor, 1000, () => {
console.log(processor.result());
})
Extract script. Use web worker API functions *
Use Web Workers
const worker = new Worker('/path/to/script.js');
worker.postMessage('start')
worker.onmessage = (e) => {
console.log(e.data)
}
// ..
self.addEventListener('message', (e) => {
if (e.data === 'start') {
for (let i = 1; i < times; i++) {
processor.calculate(i);
}
postMessage(
processor.result()
)
}
}, false);
Create Worker instance. Post "start" message
Resources
Used resources
For further reading
Any questions?
Event Loop in the browser Javascript
By Ufocoder
Event Loop in the browser Javascript
- 3,538