What is the best way to execute intensive javascript without blocking? - javascript

So, I'm loading a 3d model using webgl, and have some code to perform some operations on it before displaying it.
The problem is that this takes on the order of seconds and completely blocks the user from doing anything in the meantime. It's bad enough that you can't even scroll during this period. I've heard there's no such thing as multithreading in javascript, but I need someway for it not to block the main thread.
I even tried a setup where I load it in an iframe and using window.postMessage and a message event listener, but it seems the frame's domain uses the same thread for its javascript as well so that didn't work. Anyone else have a solution for dealing with CPU intensive code so that the user isn't blocked from doing anything?

There really isn't an easy answer at least at the moment
WebWorkers
Web worker run JavaScript in another thread. Unfortunately they are extremely limited it what they are allowed to do. Generally all you can do is pass messages back and forth. A WebWorker can not touch the DOM, it can not do WebGL or make Canvas (yet). All you can really do currently is networking and passing strings and/or typed arrays to and from a WebWorker.
If the thing you are doing that takes lots of time can be passed back to the main thread in a JSON string and/or typed arrays this might work for you.
A state machine
A common way to handle this in JavaScript is to make your loader do things over several states so that you can call it something like this
function doALittleMoreWork() {
...
if (theresStillMoreWorkToDo) {
setTimeout(doALittleMoreWork, 16);
}
}
Or something along those lines. doALittleMoreWork does a portion of the work and then remembers enough state so when called again it can continue where it left off. This is how the O3D loader worked.
You could try to use ES6 generators.
Generators effectively let you create a state machine super easy. Browsers don't yet support ES6 but there are libraries that let you use this feature now like for example Google Traceur
In fact if you write a simple generator like
function *foo() {
console.log("do first thing");
yield 1;
console.log("do 2nd thing");
yield 2;
console.log("do 3rd thing");
yield 3;
console.log("do 4th thing");
yield 4;
console.log("do 5th thing");
yield 5;
}
and you run it through traceur you'll see how it turns it into a state machine for you like #2 above.

Web workers API is definitely what you need

Related

What is the best way to schedule a small task to execute as fast as possible, while still making it wait for UI updates?

I'm reading a bit about micro tasks and such, because at the moment I have a project that is poorly optimized and some tasks make the UI hang.
I've solved 95% of that by using a (Service) Worker for the heaviest tasks. But there's still some code that just has to be on the main thread and I'm wondering what the best way is to optimize that code.
I basically have 2 wishes:
I want a function to wait a little bit before executing, just enough to let the browser do any necessary UI drawings / changes.
But after that, I do want that function to execute as soon as possible. If I can prevent it, I don't want the task to be put at the very end of the browser's task queue. The reason for this is because the function changes the value of a variable and other functions down the line would benefit from having the latest update of the value of that variable.
After reading about micro tasks, I'm not sure if they're the right tool for the job. Because as far as I understand, the browser's decision on when to run a microtask has not so much to do with the UI, but more with what other macro tasks are on the task queue.
These are the alternatives that I've been able to find:
setTimeout() with a timeout of 0, I've read that the browser will automatically increase the time-out if it needs to.
requestAnimationFrame(), but that seems to always wait at least 1/60th of a second. If the UI is done sooner than that, then I'd want my function run sooner than that.
requestIdleCallback() sounds perfect, except that it's not supported by all browsers.
using new MessageChannel() to send an empty message from one port to the other; then executing the task when the 2nd port receives the message. The only reason I'm considering this one is because apparently Facebook uses it in React to queue a task in the browser. On Node React uses setImmediate(), but since that doesn't exist in the browser, apparently Facebook's developers thought this was the best alternative. And because I'm assuming they're cleverer than me, I think there must be something to it, right?
If web workers are an option (see comlink or do vanilla implementation) otherwise see the rest of my answer.
Considering your wish #1, I understand that you want consistent frame rate i.e. use only ideal time and next critical UI task should supersede your task.
All the options that you have listed out will not work out if the task runs longer than time for next Paint. Once the task is running it will have to end before browser can do anything else. So the problem is not with scheduling per say but preempting the task for more critical UI tasks.
In terms of scheduling both setTimeout and requestIdleCallback will not satisfy your wish #2 as, setTimeout will push new task at the end of the queue and requestIdleCallback (being worse of two in terms of priority) will wait for all the tasks to complete.
There is no straight forward way to automatically preempt a task in javascript as of now (to best of my knowledge). Even libraries like React can cause UI throttling if pushed hard enough.
Solutions that we have at hand.
Recursive SetTimeout.
Generator functions with some form of scheduling.
In both scenario you will be responsible for breaking down the task in smaller chunks that can "hopefully" complete before next UI paint.
Both work in same fashion pretty much with only difference being that you will need to chain tasks in setTimeout and in generator you can yield. The generator approach is way better in my opinion in terms of syntax as you will just need to add yield statement at different places. Not ideal but better.
Generator Example.
function* task = () => {
let i = 0;
while (true) {
i = i+1 % 10;
yield;
}
}
const callback = () => {
task.next();
setTimeout(callback, 0);
}
setTimeout(callback, 0);
P.S. Personally I wish there was a simple solution, I've looked for one for a long time. Let me know if this answers your question, if you have any doubts or you find anything interesting around this topic.

Excessive use of window.setTimeout

So I have this single page application that does a lot of computation every time the user does an action like click a button. As javascript is a not threaded, the lengthy calc blocks the UI updates and creates a bad user experience:
$('update-btn').click() {
updateDomWithAnimation();
doLenghtyCalc();
}
After reading perhaps one too many articles on how to handle this, I find that wrapping some of the function calls with window.setTimeout does help. So armed with this knowledge I have started wrapping them up and it seems to bring some responsiveness back to the browser.
However my question is, are there any adverse side effects of having too many timeout statements even if the delay is only 0 or 1? From a business logic perspective I am making sure only independent standalone functions are wrapped in setTimeout. I wanted to check from a technical viewpoint. Can any JS gurus share some insight?
P.S: I had taken a look at Web Workers, but my code is built using Jquery and depends heavily on DOM states etc so implementing web workers atm would not be possible which is why I am using timeouts
Much appreciated!
While technically it's ok to have several timeouts running it's generally advisable to not have too many.
One thing we did was to have a single timeout/setInterval each that when fired runs a set of functions that can be added or removed at anytime.
///Somewhere
var runnableFunctions = []
var runningIntervalID = window.setInterval(function() {
runnableFunctions.forEach(function(func) {
if(typeof func === 'function') {
func.call(null);
}
}, 1);
/// Elsewhere
$(domElem).on(event, function() {
runnableFucntions.push(function() {
//Do something on interval
// slice out this function if it only needs to run once.
});
});
This is just a dirty example but you get the idea where you can shove functions into an array and have them run in a single timeout/interval vs setting up many timeouts/intervals and then remembering to stop them later.

Forever loop while waiting for asynchronous task?

I'm wondering if there's a way to cause JavaScript to wait for some variable-length code execution to finish before continuing using events and loops. Before answering with using timeouts, callbacks or referencing this as a duplicate, hear me out.
I want to expose a large API to a web worker. I want this API to feel 'native' in the sense that you can access each member using a getter which gets the information from the other thread. My initial idea was to compile the API and rebuild the entire object on the worker. While this works (and was a really fun project), it's slow at startup and cannot show changes made to the API without it being sent to the worker again after modification. Observers would solve part of this, and web workers transferrable objects would solve all, but they aren't adopted widely yet.
Since worker round-trip calls happen in a matter of milliseconds, I think stalling the thread for a few milliseconds may be an alright solution. Of course I would think about terminating in cases where calls take too long, but I'm trying to create a proof of concept first.
Let's say I want to expose the api object to the worker. I would define a getter for self.api which would fetch the first layer of properties. Each property would then be another getter and the process would continue until the final object is found.
worker.js
self.addEventListener('message', function(event) {
self.dataRecieved = true;
self.data = event.data; // would actually build new getters here
});
Object.defineProperty(self, 'api', {
get: function() {
self.dataRecieved = false;
self.postMessage('request api first-layer properties');
while(!self.dataRecieved);
return self.data; // whatever properties were received from host
}
});
For experimentation, we'll do a simple round-trip with no data processing:
index.html (only JS part)
var worker = new Worker("worker.js");
worker.onmessage = function() {
worker.postMessage();
};
If onmessage would interrupt the loop, the script should theoretically work. Then the worker could access objects like window.document.body.style on the fly.
My question really boils down to: is there a way to guarantee that an event will interrupt an executing code block?
From my understanding of events in JavaScript, I thought they did interrupt the current thread. Does it not because it's executing a blank statement over and over? What if I generated code to be executed and kept doing that until the data returned?
is there a way to guarantee that an event will interrupt an executing code block
As #slebetman suggests in comments, no, not in Javascript running in a browser's web-worker (with one possible exception that I can think of, see suggestion 3. below).
My suggestions, in decreasing order of preference:
Give up the desire to feel "native" (or maybe "local" might be a better term). Something like the infinite while loop that you suggest also seems to be very much fighting agains the cooperative multitasking environment offered by Javascript, including when thinking about a single web worker.
Communication between workers in Javascript is asynchronous. Perhaps it can fail, take longer than just a few milliseconds. I'm not sure what your use case is, but my feeling is that when the project grows, you might want to use those milliseconds for something else.
You could change your defined property to return a promise, and then the caller would do a .then on the response to retrieve the value, just like any other asynchronous API.
Angular Protractor/Webdriver has an API that uses a control flow to simulate a synchronous environment using promises, by always passing promises about. Taking the code from https://stackoverflow.com/a/22697369/1319998
browser.get(url);
var title = browser.getTitle();
expect(title).toEqual('My Title');
By my understanding, each line above adds a promise to the control flow to execute asynchronously. title isn't actually the title, but a promise that resolves to the title for example. While it looks like synchronous code, the getting and testing all happens asynchronously later.
You could implement something similar in the web worker. However, I do wonder whether it will be worth the effort. There would be a lot of code to do this, and I can't help feeling that the main consequence would be that it would end up harder to write code using this, and not easier, as there would be a lot of hidden behaviour.
The only thing that I know of that can be made synchronous in Javascript, is XMLHttpRequest when setting the async parameter to false https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#Parameters. I wonder if you could come up with some sort of way to request to the server that maintains a connection with the main thread and pass data along that way. I have to say, my instinct is that this is quite an awful idea, and would be much slower than just requesting data from the main thread.
For what I know, there is not something native in JS to do this but it is relatively easy to do something similar. I made one some time ago for myself: https://github.com/xpy/whener/blob/master/whener.js .
You use it like when( condition, callback ) where condition is a function that should return true when your condition is met, and callback is the function that you want to execute at that time.

Asynchronous programming in javascript (NOT AJAX)

Is it possibly to do things asynchronously in javascript (AJAX aside)? For example, to iterate multiple arrays at the same time. How is it done? A brief example would be nice. Searching for this was hard, due to all the ajax pollution, which is not what I am looking for.
Thanks in advance.
Use web Workers. But remember that it is a very new feature and not all browsers are fully supported.
You could use setTimeout.
setTimeout(function () { iterateArray(array1); reportDone(1); }, 0);
setTimeout(function () { iterateArray(array2); reportDone(2); }, 0);
I'm not sure how concurrent it will be, but it is an asynchronous programming model.
As stated by Grumdrig you can write code like this:
setTimeout(function () { iterateArray(array1); reportDone(1); }, 0);
setTimeout(function () { iterateArray(array2); reportDone(2); }, 0);
But it will still not run concurrently. Here's a general idea of what happens after such timeouts are called:
Any code after the setTimeout calls will be run immediately, including returns to calling functions.
If there are other timers in queue that are at or past their delay or interval time, they will be executed one at a time.
While any timer is running, another might hit its interval/delay time, but it will not be run until the last one is finished.
Some browsers give priority to events fired from user interaction such as onclick and onmousemove, in which case the functions attached to those events will execute at the expense of timer accuracy.
This will continue until there is an opening (no previously called timers or event handlers requesting execution). Only then will the functions in the example code be run. Again one at a time, with the first one likely but not certainly executing first. Also, I'm venturing a guess that some browsers might impose a minimum delay time, which would make any timers set with a delay of 0 milliseconds be run even later than expected.
Obviously there is no performance advantage to running code like this. In every case it will make things take longer to complete. However in cases where a single task is taking so long it freezes the browser (and possibly trips "Script is taking too long" browser warnings), it can be helpful to break it up into smaller faster executing pieces that run sequentially after some delay time, thus giving the browser some time to breathe.
Web Workers have been mentioned, and if you are not concerned about IE compatibility then you can use them for true concurrency. However there are some severe limitations on their use imposed for security reasons. For one they cannot interact with the DOM in any way, meaning any changes to the page still must be done synchronously. Also all data passed to and from workers is serialized in transit, meaning true Javascript objects cannot be used. That being said, for intensive data processing, Web Workers are probably a better solution than breaking a function up into multiple timer delayed tasks.
One new development in this field is HTML5 Web Workers.
JavaScript is normally single threaded; you cannot do several things at once. If your JavaScript code is too slow, you will need to offload the work. The new way is to use web workers, as others have noted. The old way is often to use AJAX and do the work on the server instead. (Either with web workers or with AJAX, the arrays would have to be serialized and the result deserialized)
I have to agree with MooGoo, i also wonder why you would run through such a big array in one go.
There's an extension to JavaScript called StratifiedJS, it allows you do multiple things at once as long as they're asynchronous. Also, webworkers are an awkward "solution" that just make things more complicated, also, they don't work in IE.
In StratifiedJS you could just write.
waitfor {
// do something long lasting here...
}
and {
// do something else at the same time...
}
// and when you get here, both are done

Prevent long running javascript from locking up browser

I have JavaScript which performs a whole lot of calculations as well as reading/writing values from/to the DOM. The page is huge so this often ends up locking the browser for up to a minute (sometimes longer with IE) with 100% CPU usage.
Are there any resources on optimising JavaScript to prevent this from happening (all I can find is how to turn off Firefox's long running script warning)?
if you can turn your calculation algorithm into something which can be called iteratively, you could release control back the browser at frequent intervals by using setTimeout with a short timeout value.
For example, something like this...
function doCalculation()
{
//do your thing for a short time
//figure out how complete you are
var percent_complete=....
return percent_complete;
}
function pump()
{
var percent_complete=doCalculation();
//maybe update a progress meter here!
//carry on pumping?
if (percent_complete<100)
{
setTimeout(pump, 50);
}
}
//start the calculation
pump();
Use timeouts.
By putting the content of your loop(s) into separate functions, and calling them from setTimeout() with a timeout of 50 or so, the javascript will yield control of the thread and come back some time later, allowing the UI to get a look-in.
There's a good workthrough here.
I had blogged about in-browser performance some time ago, but let me summarize the ones related to the DOM for you here.
Update the DOM as infrequently as possible. Make your changes to in-memory DOM objects and append them only once to the DOM.
Use innerHTML. It's faster than DOM methods in most browsers.
Use event delegation instead of regular event handling.
Know which calls are expensive, and avoid them. For example, in jQuery, $("div.className") will be more expensive than $("#someId").
Then there are some related to JavaScript itself:
Loop as little as possible. If you have one function that collects DOM nodes, and another that processes them, you are looping twice. Instead, pass an anonymous function to the function that collects the nodes, and process the nodes as your are collecting them.
Use native functionality when possible. For example, forEach iterators.
Use setTimeout to let the browser breathe once in a while.
For expensive functions that have idempotent outputs, cache the results so that you don't have to recompute it.
There's some more on my blog (link above).
This is still a little bit bleeding edge, but Firefox 3.5 has these things called Web Workers, I'm not sure about their support in other browsers though.
Mr. Resig has an article on them here: http://ejohn.org/blog/web-workers/
And the Simulated Annealing is probably the simplest example of it, if you'll notice the spinning Firefox logo does not freeze up, when the worker threads are doing their requests (thus not freezing the browser).
You can try performing long running calculations in threads (see JavaScript and Threads), although they aren't very portable.
You may also try using some Javascript profiler to find performance bottlenecks. Firebug supports profiling javascript.
My experience is that DOM manipulation, especially in IE, is much more of an issue for performance than "core" JavaScript (looping, etc.).
If you are building nodes, it is much faster in IE to do so by building an HTML string and then setting innerHTML on a container than by using DOM methods like createElement/appendChild.
You could try shortening the code by
$(xmlDoc).find("Object").each(function(arg1) {
(function(arg1_received) {
setTimeout(function(arg1_received_reached) {
//your stuff with the arg1_received_reached goes here
}(arg1_received), 0)
})(arg1)
}(this));
or for "for" loops try
for (var i = 0 ; i < 10000 ; i = i + 1) {
(function(arg1_received) {
setTimeout(function(arg1_received_reached) {
//your stuff with the arg1_received_reached goes here
}(arg1_received), 0)
})(arg1_to_send)
}
I had the same problem and my customers was reporting this as "Kill page" error. But now I juz got a best solution for that. :)

Categories