tags : Web Development, Javascript, DOM
FAQ
Javascript Engines
- Chrome: v8 + UI part, Skia
- Firefox: Spidermonkey, webrender
- Node.js: V8 + I/O parts
Concurrency in Javascipt
- See Concurrency
- The single-threaded model has made Node.js a popular choice for server-side programming due to its non-blocking IO, making handling a large number of database or file-system requests very performant.
- However, CPU-bound (computationally intensive) tasks that’s pure JavaScript will still block the main thread.
- To achieve real paralleling, you may need to use workers.
- See Web workers
Communication between runtimes
- These can communicate using postMessage
- Eg. iframes, webworkers etc etc.
Why setTimeout is not accurate?
- Event loop only runs after the
call stack
is empty setTimeout
is not aguaranteed-time-to-execution
but instead aminimum-time-to-execution
Runtime
- Only do one thing at a time, single threaded.
- UI updates handled in the same main thread.
When your code is executed
- If it’s synchronous call
- it’ll be in the
call stack
and it’ll run to completion. - These can make asyc calls.
- it’ll be in the
- If it’s async external/browser API call
- If
callbacks
, it will add your callback code to theMacrotask queue
- If
promises.then()
, it will add your then code to theMicrotask queue
- If
- As soon as the
call stack
has finished current tick (stack empty)- The
event loop
feeds it with anew tick
new tick
1 task
(oldest) fromMacrotask queue
All tasks
fromMicrotask queue
All/Partial tasks
fromRender queue
- The
- Order of processing
new tick
- Process all of
microtask queue
(incl. newly added during run) - Process
Render queue
- Process
Macrotask queue
w theoldest task
- Process all of
Event Loop
Types
Browser
- Window(each
origin
gets its own eventloop) - Worker
- Worklet (See Web workers)
NodeJS
- Main loop/Main thread
- Internally there’s no queue. It’s epoll!
- Worker pool/Thread pool (Implemented using libuv, See O)
- This uses an actual queue
- Node.js automatically uses the Worker Pool to handle cpu/io expensive tasks.
- NodeJS’s synchronous APIs are intended for scripting use, not to be used in server context because they block the event loop
Event loop components
JavaScript has a runtime model based on an event loop. It’s responsible for executing the code, collecting and processing events, and executing queued sub-tasks
Call stack
- When writing synchronous code, we just use the stack.
- Pushes and Pops functions in and out of it
one thread = one call stack = one thing at a time
- Run to completion
- whenever a function runs, it cannot be preempted and will run entirely before any other code runs.
- This differs from C, for instance, where if a function runs in a thread, it may be stopped at any point by the runtime system to run some other code in another thread.
Macro task/Callback/Event Queue
- Every time you add a callback (ex. setTimeout/AJAX APIs/input events), it is added to this queue
Microtask Queue/Job queue
Promise
initialize and resolve are synchronous, only.then
callbacks are sent tomicrotask queue
- Callbacks added with
then()
ormutation observer
will never be invoked before the completion of the current run of the event loop. Even already-resolved promises will be invoked only after the current run of the event loop for consistency.Promise.resolve().then(() => console.log(2)); console.log(1); // Logs: 1, 2
- It is a prioritized queue
- Execute this code later (asynchronously)
- But as soon as possible! (before the next Event Loop)
- i.e
microtask queue
will be executed beforemacrotask queue
andrender queue
Blocking the event loop
- Since
event loop
can only run when thecall stack
is empty. If we do not allow thecall stack
to be empty, we’ll be blocking theevent loop
.- i.e Running synchronous code will keep blocking the
event loop
. - The
event loop
putsrender queue
calls to the stack - if the
call stack
is not empty it does not get a chance torender queue
so our browsers sort of pause when synchronous things are happening.
- i.e Running synchronous code will keep blocking the
- Running the
current tick
contains thefull microtask queue
- You can block the
event loop
from going to thenext tick
if you continuously add new tasks to themicrotask queue
- microtasks can enqueue new microtasks and those new microtasks will execute before the end of the current event loop iteration.
- You can block the
DOM Parsing
Whole DOM creation process
- DOM parsing
- Parse the HTML code into a machine-understandable tree, called DOM (Document Object Model)
- CSS parsing
- Parse the CSS code into another machine-understandable tree.
- CSSOM creation
- Merge between the previously parsed DOM and CSS, called CSSOM (CSS Object Model).
- Contains the information of all the DOM elements, along with their styles (critical for the next step, keep reading);
- Render tree creation
- Cleaned CSSOM
- Eg. Some DOMNode is styled with
display: none;
in the CSSOM, it won’t be in this tree as it won’t be rendered.
- Layouting
- Process of calculating the sizing and positions of every single element from the Render tree
- Also referred as
Reflow
- Layer tree creation
- It is not granted that there will be only one final raster of the page.
- The browser (or you using some imperative styling rule ex.
will-change: transform;
, not to pre-optimize) can decide that for the given page it is better to split it into different layers (rasters). - This is usually done for performance reasons.
- If a raster has to be changed many times while the others must remain unchanged
- Creating layers(rasters) can avoid un-needed repaints of un-changed items.
- This process figures out how many layers need to be created and which elements will fall inside each one;
- Painting
- At this stage we know everything of our code
- Everything has been reordered into digestible trees and we are ready to actually paint the pixels in rasters (Chrome: Skia, Firefox: webrender)
- GPU sync
- The fresh painted rasters from the Painting stage are sent to the GPU memory with all the necessary management information
- Composition
- The GPU positions every single raster in the right position and with the right effects applied (ex. opacity), creating the final frame on the screen.
More on Render Queue
- Read this
The Rendering UI Queue is the process by which:
- Browsers manage to transform your abstract code (HTML + CSS + JavaScript) into raster images to show to the end user.
- This process is sometimes referred also as
rendering path
orcritical rendering path
- This process consists of a long pipeline of different operations needed to interpret the code
- Respond to animations and user interactions (events)
- Keep everything consistent
- Create the final raster(s) for the given frame
- Sync with the GPU (send the so-constructed raster to the GPU, which will take care of displaying it on the screen).
- This pipeline has not to be executed in his totality, in order to save precious time and gain performance, the browser executes only the strictly needed operations.
Optimizing renders
Avoid layout thrashing
//The wrong way:
for ( let i = 0; i < 10; i++ ) {
changeHeight();
readWidth();
}
//The right way:
//Using two loops is way more performant than forcing reflows!
for ( let i = 0; i < 10; i++ ) {
//Do all the changes first.
changeHeight();
}
for ( let i = 0; i < 10; i++ ) {
//Read all them after.
readWidth();
}
Use appropriate CSS properties
- Different CSS properties trigger different phase of the rendering pipeline.
- We would usually want to avoid
reflow
by using properties that would only triggercomposition
orpaint
which will result in faster websites. Eg. For instance, a change in background-image doesn’t require any layout changes, but does require paint and composite. - If you must use a property that triggers layout or paint, it is unlikely that you will be able to make the animation smooth and high-performance.
- See How CSS works - Learn web development | MDN
- See CSS Triggers | CSS-Tricks - CSS-Tricks
- See Rendering Performance
- See Stick to Compositor-Only Properties and Manage Layer Count
- See Eliminate content repaints with the new Layers panel in Chrome
- See How to create high-performance CSS animations
Memory management
- Understanding JS memory management also helps in understanding closures.
Heap and Stack
- There’s no practical stack/heap distinction in JS. That would be an implementation detail of the JS engine.
- Function calls, however, work on what is referred to as the
call stack
, much as in most languages.- This is not the memory stack in languages like golang.
Arguments
andlocal variables
may continue to exist after afunction
is out of thecall stack
because they are stored outside thecall stack
Allocations
- Declaring a function also allocates memory as function is just a callable object
- Function expressions also allocate an object. (eg. specifying a function to
addEventListener
) - Some function calls result in object allocation. (eg.
new Date()
)