Engines in Browser - How do they interact? - javascript

I've been trying to learn more under the hood when it comes to Web Development, specifically JavaScript, and how the different engines interact with the JavaScript engine. For example, I know that the Rendering Engine is interacting, HTTP Requests, etc. I was just wondering, when the JavaScript Engine has to send data to another engine, such as the rendering engine, how much more computing has the JavaScript engine done by the time that data gets there?

The Web is a standardised platform running on the application layer of the Internet Protocol Suite.
It is a hypermedia system consisting of a protocol (HTTP), a markup language, a styling language (CSS) and a browser API (the Web API).
The Web was a development upon earlier hypermedia systems like HyperCard and uses a similar single-threaded, event-oriented rendering process.
The crux of your question is "how do the various parts of the browser interact to render a web page?"
The answer is a mixture of vendor-specific implementation and standardised behaviour defined by the standards bodies W3C and WHATWG.
The rendering process at a high level might be something like this:
The inbound message is exposed to the browser application by the operating system's networking substem (which in turn received the stream over the TCP IPS transport-layer) as a stream of bytes encoded using UTF-8.
Thus the networking subsystem of the browser receives an inbound UTF-8 encoded octet stream.
The browser will likely process the inbound stream of bytes on a thread or process other than that coordinating the rendering of the UI to avoid locking up the browser.
The browser understands the text-based HTTP protocol Tim Berners-Lee invented in 1989, and interprets the inbound bytes as an HTTP message.
The body of the message will correspond to the markup of the page. Once the page markup has been received it will be handed to a thread for rendering.
Starting at the top of the markup the browser rendering process will begin parsing according to an algorithm defined by the W3C/WHATWG.
When JavaScript is encountered, it will typically be run immediately, but there are complicated rules about exactly what happens when.
When references to resources (eg. images, scripts, stylesheets) are encountered, requests for their download will be made. Some will block the rendering process, some will not.
When a piece of JavaScript is encountered, its evaluation will usually block the user interface. This is because the Web was designed to have a single thread of execution controlling the rendering of a page, and JavaScript is part of that process. As far as I know, multi-threaded user interface rendering systems are unusual because of their complexity (although I could be wrong on this).
So the browser will extract the JavaScript from the markup (or a linked script resource), and hand it to the JavaScript engine for evaluation. The JavaScript runtime is an application that enables the evaluation of a stack-based language. It has a stack and a heap and logic for converting JavaScript script into a form understood by the CPU.
The stack has zero or more stack frames on it. In JavaScript stack frames are called execution contexts. An execution context is like a bookmark in a book, it helps the runtime keep track of where it is in the execution of the script. Only when the stack is empty of execution contexts can the browser continue with its rendering work - so yes, running JavaScript typically blocks rendering.
Communication between the various browser subsystems (network, JavaScript runtime, rendering) will occur via the heap allocated to the browser process, or if the browser is multi-process, by inter-process communication mechanisms exposed by the particular operating system being used (eg. named pipes).
Once enough (according to the W3C specification) of the markup (HTML) and styles (CSS) and enough of the script (JavaScript) has been evaluated, rendering can begin.
Rendering is a vendor-specific process, but most browsers will be similar at a high level. Each HTML element is processed in turn. Chrome uses the following sequence for every element on the page:
The JavaScript that applies to the element is evaluated.
The CSS styles that apply to the element are evaluated.
The element is laid-out on the page accordingly.
The element is painted into a bitmap (colored pixels).
The element is composited. It's final appearance it calculated according to the impact of the different layers of other elements on the page.
This process might be repeated more than once on any given element, depending on the contents of the page and changes introduced dynamically by scripts.

Computers are normally able to perform billions of calculations every second. However, the rendering thread only updates 60 times normally (refresh rate/VSync), and network requests are much slower. Offloading them to thread is cheaper compared to being blocked by waiting.
An additional reason to offload drawing is to make the interface look snappy: even if something is running long in the JS thread, an offloaded rendering thread will still be responsive as normal.

Very roughly, this is how it goes:
The browser gets the HTML page (or HTML data what was sent to it).
Browser begins to interpret and process the page
In the head section, if it sees any external linked files, such as JavaScript & CSS files, it initiates a load request immediately.
If there is any JavaScript, either in the HTML, or in a linked file, the JavaScript is executed immediately, unless it takes advantage of the newer async option. This is why JavaScript normally includes an event handler for when the document has finished loading.
The rest goes on …
Taking a break, note that the head stuff is processed as it is received, not later.
The body content is processed, from top to bottom.
While this is happening the DOM (Document Object Model — a sort of map of the contents) is created.
HTML visible content is rendered.
Any additional external data, such as images will initiate their own download requests. This content is loaded in parallel, rather than waiting. This is why badly designed HTML often leads to redrawing after images have been loaded.
The CSS, which is loaded by now, is applied at the same time.
That’s it for static content. There is one more stage, if you have JavaScript which changes content:
JavaScript can change the content of the Document at any time.
Often, but not always, changing the content can trigger a redraw. This will be needed if the change affects the layout.
JavaScript can also change individual CSS styles of HTML elements. Again, this may trigger a redraw.

Related

is there any way to tell v8 to preserve p-code for the next 'page' that loads the same javascript library

Scenario: In order to make lighthouse analytics happy (ie reduce download, blocking, etc. etc. etc.) a web app has been chopped up so that previously in-page dialogues/sequences
are now independent, subsequently loaded pages.
Naturally this means that a single library link download has now become a download for each of the resulting dialog pages. As far as download size, any caching mechanism will ameliorate any multiplier. Although v8/closure has been used to massively shrink the download size - this leaves the blocking problem of reparsing and recompiling for every instance (when only one parse/compile on the target/client should actually be needed, if we were optimal).
So, the question is, is there any mechanism to flag library code such that the v8 engine
retains and reuses the p-code it parsed the first time?? [Note, a ServiceWorker implementation is not the answer for the library/framework.]
V8, in combination with the way it's embedded into Chrome, does have the ability to cache code. You don't need to flag anything; just like HTTP caching, the browser has certain heuristics to decide by itself whether/when to cache things.

Why doesn't JavaScript get its own thread in common browsers?

Not enough that JavaScript isn't multithreaded, apparently JavaScript doesn't even get its own but shares a thread with a load of other stuff. Even in most modern browsers JavaScript is typically in the same queue as painting, updating styles, and handling user actions.
Why is that?
From my experience an immensely improved user experience could be gained if JavaScript ran on its own thread, alone by JS not blocking UI rendering or the liberation of intricate or limited message queue optimization boilerplate (yes, also you, webworkers!) which the developer has to write themselves to keep the UI responsive all over the place when it really comes down to it.
I'm interested in understanding the motivation which governs such a seemingly unfortunate design decision, is there a convincing reason from a software architecture perspective?
User Actions Require Participation from JS Event Handlers
User actions can trigger Javascript events (clicks, focus events, key events, etc...) that participate and potentially influence the user action so clearly the single JS thread can't be executing while user actions are being processed because, if so, then the JS thread couldn't participate in the user actions because it is already doing something else. So, the browser doesn't process the default user actions until the JS thread is available to participate in that process.
Rendering
Rendering is more complicated. A typical DOM modification sequence goes like this: 1) DOM modified by JS, layout marked dirty, 2) JS thread finishes executing so the browser now knows that JS is done modifying the DOM, 3) Browser does layout to relayout changed DOM, 4) Browser paints screen as needed.
Step 2) is important here. If the browser did a new layout and screen painting after every single JS DOM modification, the whole process could be incredibly inefficient if the JS was actually going to make a bunch of DOM modifications. Plus, there would be thread synchronization issues because if you had JS modifying the DOM at the same time as the browser was trying to do a relayout and repaint, you'd have to synchronize that activity (e.g. block somebody so an operation could complete without the underlying data being changed by another thread).
FYI, there are some work-arounds that can be used to force a relayout or to force a repaint from within your JS code (not exactly what you were asking, but useful in some circumstances).
Multiple Threads Accessing DOM Really Complex
The DOM is essentially a big shared data structure. The browser constructs it when the page is parsed. Then loading scripts and various JS events have a chance to modify it.
If you suddenly had multiple JS threads with access to the DOM running concurrently, you'd have a really complicated problem. How would you synchronize access? You couldn't even write the most basic DOM operation that would involve finding a DOM object in the page and then modifying it because that wouldn't be an atomic operation. The DOM could get changed between the time you found the DOM object and when you made your modification. Instead, you'd probably have to acquire a lock on at least a sub-tree in the DOM preventing it from being changed by some other thread while you were manipulating or searching it. Then, after making the modifications, you'd have to release the lock and release any knowledge of the state of the DOM from your code (because as soon as you release the lock, some other thread could be changing it). And, if you didn't do things correctly, you could end up with deadlocks or all sorts of nasty bugs. In reality, you'd have to treat the DOM like a concurrent, multi-user datastore. This would be a significantly more complex programming model.
Avoid Complexity
There is one unifying theme among the "single threaded JS" design decision. Keep things simple. Don't require an understanding of a multiple-threaded environment and thread synchronization tools and debugging of multiple threads in order to write solid, reliable browser Javascript.
One reason browser Javascript is a successful platform is because it is very accessible to all levels of developers and it relatively easy to learn and to write solid code. While browser JS may get more advanced features over time (like we got with WebWorkers), you can be absolutely sure that these will be done in a way that simple things stay simple while more advanced things can be done by more advanced developers, but without breaking any of the things that keep things simple now.
FYI, I've written a multi-user web server application in node.js and I am constantly amazed at how much less complicated much of the server design is because of single threaded nature of nodejs Javascript. Yes, there are a few things that are more of a pain to write (learn promises for writing lots of async code), but wow the simplifying assumption that your JS code is never interrupted by another request drastically simplifies the design, testing and reduces the hard to find and fix bugs that concurrency design and coding is always fraught with.
Discussion
Certainly the first issue could be solved by allowing user action event handlers to run in their own thread so they could occur any time. But, then you immediately have multi-threaded Javascript and now require a whole new JS infrastructure for thread synchronization and whole new classes of bugs. The designers of browser Javascript have consistently decided not to open that box.
The Rendering issue could be improved if desired, but at a significant complication to the browser code. You'd have to invent some way to guess when the running JS code seems like it is no longer changing the DOM (perhaps some number of ms go by with no more changes) because you have to avoid doing a relayout and screen paint immediately on every DOM change. If the browser did that, some JS operations would become 100x slower than they are today (the 100x is a wild guess, but the point is they'd be a lot slower). And, you'd have to implement thread synchronization between layout, painting and JS DOM modifications which is doable, but complicated, a lot of work and a fertile ground for browser implementation bugs. And, you have to decide what to do when you're part-way through a relayout or repaint and the JS thread makes a DOM modification (none of the answers are great).

Is it still worth using eval for performance on mobile JavaScript?

To combine all modules into a single resource, we wrote each module into a separate script tag and hid the code inside a comment block (/* */). When the resource first loads, none of the code is parsed since it is commented out. To load a module, find the DOM element for the corresponding script tag, strip out the comment block, and eval() the code....
On an iPhone 2.2 device, 200k of JavaScript held within a block comment adds 240ms during page load, whereas 200k of JavaScript that is parsed during page load added 2600 ms. That's more than a 10x reduction in startup latency by eliminating 200k of unneeded JavaScript during page load!
http://googlecode.blogspot.co.uk/2009/09/gmail-for-mobile-html5-series-reducing.html
https://developers.google.com/speed/docs/best-practices/mobile
The gmail article is more than three years old and there's been great advantages in mobile performance since then, namely things like iOS's Nitro and JIT coming to mobile. Are the performance gains still to be had from using eval?
Its not the same technology issue as it was before since JavaScript engines have become so performant. Rather there are other considerations in terms of being more app-like.
There are tricks now that are different in approach such as using web workers for ajax requests to free up the thread, utilizing the GPU with CSS transformations and requestAnimationFrame or even asm.js. Using localStorage/sessionStorage and Application Cache is another approach along those lines where you can really get a lot of client-side caching up front to avoid calling anything more than the content JSON / images data urls / videos and load/execute things into memory as needed from those caches.
Its a different time in other words and your question is interesting but not focused in the right areas to really make a difference in web-app performance.

Why exactly is server side HTML rendering faster than client side?

I am working on a large web site, and we're moving a lot of functionality to the client side (Require.js, Backbone and Handlebars stack). There are even discussions about possibly moving all rendering to the client side.
But reading some articles, especially ones about Twitter moving away from client side rendering, which mention that server side is faster / more reliable, I begin to have questions. I don't understand how rendering fairly simple HTML widgets in JS from JSON and templates is a contemporary browser on a dual core CPU with 4-8 GB RAM is any slower than making dozens of includes in your server side app. Are there any actual real life benchmarking figures regarding this?
Also, it seems like parsing HTML templates by server side templating engines can't be any faster than rendering same HTML code from a Handlebars template, especially if this is a precomp JS function?
There are many reasons:
JavaScript is interpreted language and is slower than server side
(usually done in compiled language)
DOM manipulation is slow, and if you are manipulating it in JS it
results in poor performance. There are ways to overcome this like
preparing your rendering in text then evaluating it, this might in fact gets you as close to server side rendering.
Some browsers are just too slow, especially old IE
Performance of compiled language versus interpreted javascript
Caching, ie - serving up the exact same page another user has already requested, this removes the need for each client to render it. Great for sites with huge traffic - ie news sites. Micro-caching can even provide near real-time updates, yet serve significant traffic from the cache. No need to wait for client rendering
Less reliance on users with old computers or slow / crippled browsers
Only need to worry about rendering, less reliance on how different browsers manage DOM (reliability)
But for a complex UI, client side rendering of interactions will provide a snappier user experience.
It really depends on what performance you're trying to optimise, and for how many users.
To run code on the client side it first has to be loaded. Server side code is just loaded when the server start, whereas the client code must potentially be loaded every time the page is. In any case the code must be interpreted when loading the page, even if the file is already cached. You might also have caching of JS parse trees in the browser, but I think those are not persisted, so they won't live long.
This means that no matter how fast JavaScript is (and it is quite fast) work has to be performed while the user waits. Many studies have shown that page loading time greatly affects the users perception of the sites quality and relevance.
Bottom line is that you have 500ms at the most to get your page rendered from a clean cache on your typical developer environment. Slower devices and networks will make that lag just barely acceptable to most users.
So you probably have 50-100ms to do things in JavaScript during page load, all of it, grand total, which means that rendering a complex page, well, not easy.

Can a Native Client module render HTML generated by C++ code?

I have some data analysis code that processes an input file and outputs HTML and/or SVG. It is C++ and CPU-intensive.
I would like to create a browser plugin so that my code can be used without leaving the browser. Google's Native Client framework and the Pepper API sound interesting but perhaps is a little too restrictive. From a Native Client module is it possible to do the following via Pepper?
Access a local file on the users filesystem. I know NaCl sandbox
generally prevents this but there is surely a way to pass a
user-selected file from the Javascript layer down into NaCL?
Output HTML or SVG back to the browser. Is there any way that the C++ can modify the containing document's DOM?
i am less certain for (1), but (2) is definitely not directly possible.
for (1), the Pepper API is supposed to allow file pickers to make files available to NaCl modules, the same way that WebFS allows it for JavaScript. i'm not sure of the implementation status of this.
for (2), C++ code cannot directly manipulate the DOM. however, the NaCl module can use the PostMessage interface to send strings to JavaScript code, which can then do the actual DOM manipulation. note that in Chrome, NaCl modules are event handlers. this means that if the computation is started by some event and run synchronously (and it is CPU intensive), it would cause the browser to lose interactive responsiveness, or to become janky, since the event handler would take too long to return. a better way is to spawn a background thread to do the work in the event handler (or wake a worker thread up), and return from the event handler immediately. when the computation is done and results are ready, PostMessage the result back to let the JavaScript thread wake up (again as an event handler) and do the DOM manipulation. note that currently PostMessage must be invoked on the main thread, so the worker thread must use CallOnMainThread to wake up the main event handler thread so that it can in turn do the PostMessage.
isn't continuation passing style fun?
A file can be uploaded to the HTML file system using a file input element and JavaScript, where it can then be manipulated by both JavaScript and NaCl. The nacl_io library implements standard posix methods such as fread, so once you've saved your file it's pretty straight forward. See here for JS (don't be put off by the deprecation message, it's implemented in Chrome), and here for NaCl.
As #Bennet Yee mentioned, the NaCl module can not modify the page, but instead passes messages back to the browser window which are handled by your JavaScript. Sounds like you'll probably want to pass back some JSON data. See here for Google's docs on the messaging system.

Categories