Let's say I have a CPU intensive iframe.
It's on the same domain as the parent page, thus cross-origin does not apply, so it shares the parent's event-loop.
Is it possible to have a separate JS context (including a separate event-loop) for the iframe, such that it doesn't block the parent's UI?
Setting the sandbox
attribute for the iframe
tag doesn't solve this, although the docs state that the iframe will be considered to be from a 'unique origin'.
This question is hypothetical/conceptual. I don't have a real use-case for this. Just curious.
I was wandering how sites like JSFiddle run users' code, and apparantly it runs from an iframe from a different domain (jshell.net in JSFiddle's case).
Since “the event loop” is nothing but a programming pattern, V8 allows the ability to plug-in an external event loop implementation to work with its JavaScript runtime. Using this flexibility, the Chrome browser uses libevent as its event loop implementation, and NodeJS uses libuv to implement the event loop.
The event loop is actually composed of one or more event queues. In each queue, events are handled in a FIFO order. It's up to the browser to decide how many queues to have and what form of prioritisation to give them. There's no Javascript interface to individual event queues or to send events to a particular queue.
When the call stack is empty, the event goes through the event queue and sends the callback to the call stack. The following diagram is a proper representation of the event loop in a Node.
The Event Loop takes the timer with the shortest wait time and compares it with the Event Loop's current time. If the wait time has elapsed, then the timer's callback is queued to be called once the call stack is empty. Node. js has different types of timers: setTimeout() and setInterval() .
They don't resolve the actual problem, they just hack their way around it, by using cross-origin iframes.
For example, this can easily be seen on StackBlitz, they create a subdomain for each execution environment.
On JSFiddle, this is a bit harder to see through at first, since their iframe doesn't have a src
attribute set. However, the iframe is bound to a form, which ends up being another cross-origin iframe.
As others have mentioned correctly, you can use web workers, to generate a new JS context, and run some heavy calculations there.
This works, since web workers run in isolated threads (MDN).
As others have mentioned as well, you get a performance increase by running code in smaller chunks. Take the following example:
const array = [ ... ]; // array with hundreds of items
const result = [];
const stepByStepCalculation = () => {
if (array.length) {
result.push(doExpensiveCalculation(array[0]));
array.shift();
requestAnimationFrame(stepByStepCalculation);
}
}
stepByStepCalculation();
Now, after every iteration of the event loop, one item of our array is being processed - step by step.
Also, using requestAnimationFrame
instead of setInterval
or setTimeout
has the advantage, that it will be called after every event loop iteration, and not after ~4ms, making it much faster.
Our workarounds only work, if our expensive tasks, don't have to constantly read from the DOM.
This is a big problem, if our same-origin iframe has expensive JavaScript animations, it will heavily interfere and may cause lags in the UI.
Moving calculations into worker threads, or doing them step by step to reduce the load works.
However, to your main question, we can't create a separate JS context (as of right now) for same-origin iframes. Online JS shells tackle this problem by using cross-origin iframes.
It would be interesting to see, if we can somehow run JavaScript code in the compositor or raster thread, since they should have access to our DOM, unlike web workers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With