Is is possible to intercept browser messages such as:
Firefox:
A script on this page may be busy, or
it may have stopped responding. You
can stop the script now, open the
script in the debugger, or let the
script continue.
I.E.
A script on this page is causing your
web browser to run slowly. If it
continues to run, your computer might
become unresponsive.
These messages are occuring due to the page having alot of javascript/jquery activity.
I appricate that the fact the these messages are appearing in the first place indicate a wider problem, but is there a way of intercerping this message/situation on the client side so that a more user friendly message can be shown?
No there's no way to do this, imagine a malicious user writing a script which slows down your browser until is completely unusable, now the "Slow Script" Warning may come to the rescue, but what if he could intercept it and prevent it from being shown?
You'll need to find a solution for the underlying problem, that is, in case that you're doing a lot of calculations(which I suppose you do), you need to split those of into chunks, put them into a queue and process them either asynchronously(if possible) or in order, but with a small timeout in between.
In pseudo code it may look like this:
var queue = []; // put your functions or the data in here
function() processQueue{
if (queue.length > 0) {
var item = queue.shift() // pop of the first item of the queue
item(); // call the function, or in case of data pass it to th processing function
setTimeout(processQueue, 100); // wait 100 ms before processing the next chunck
}
}
setTimeout(processQueue, 0);
No, there is no way to intercept these messages, they are are there at a lower level in the engine to protect the user. Instead look at what's taking so long, optimize as much as possible...possibly breaking your work up into chunks that have gaps in processing so you don't trigger the messages.
Related
I setState to cause a loading spinner to appear and then call a long running operation. The operation blocks the UI, so the spinner doesn't appear. My workaround is to delay the operation by 500ms with setTimeout, so the spinner has time to appear.
Is there a better way?
I even wrote a helper function to do it:
async runAsync(message, asyncFn) {
const loadingId = this.add(message)
await _.sleep(50); // Let react update
await asyncFn();
this.remove(loadingId)
}
It's pretty suspicious for there to be a task that's expensive enough to reliably block rendering on the client. Sometimes a case can be made for it, but in most circumstances there are better approaches.
Ideally, these sorts of expensive operations would be offloaded to your server instead, if at all possible. Instead of the client doing it themselves, have the client make a request to the server, have the server perform the work, and then send the result back to the client. (If you can easily notice a delay on the computer you use for development, someone opening the website on even worse hardware - such as a phone - would have a much worse experience.)
If the above isn't possible, the other main possibility to consider would be to spawn a separate environment on the client, and perform the work in that environment - which will not interfere with rendering (such as the display of a loading spinner) on the original webpage. This can be accomplished with a web worker.. Make a request to the worker while creating the loading spinner, and then the spinner will appear immediately (and animate) while waiting for the worker to finish its processing. Then, have the worker send a message back to the parent page containing the required data.
I want to patch the alert object in the browser, to show additional text, but I need to await some data to show the necessary content in the alert. However, I can't postpone the alert call.
Also, I don't know about a way to close the alert and show another alert without a user action (if it exists, it can solve my problem too).
So, I have to await some data, but I can't break the alert behavior, which is blocking execution of the code.
To await a response, I can do something like this:
var start = performance.now();
while(true) {
var time = performance.now() - start;
if (time >= 3000) break;
}
console.log('done');
But instead of checking the timer, I will check some data.
This way should work, but this is terrible for performance, because it is the opposite to alert which is just freezing the thread and does nothing until the close dialog, and we'll load the CPU with useless work.
Is it possible freeze a thread to be more energy efficient?
I have to freeze the thread until I get some data from a worker.
Why is promise not solving your problem?
Promises is not blocking a main thread, so this is not reproducing the behavior of alert which I need.
The blocking thread is not user friendly and it's not that you need to await some data
I know about it, and this note is fair enough to development web pages and applications.
But this case is special. I develop a feature for a
browser extension, to translate alerts. The browser extension must not modify the behavior of the page. So when a web site is calling alert, the thread must be freeze. The browser extension must not postpone an alert call to avoid unexpected behavior on the page.
You can see the feature explained here: Feat: Implement optional translation of alerts and console logs #102
The only way I can think of to "block" without consuming CPU this would be to make a synchronous XMLHttpRequest (which are deprecated because blocking is not user-friendly). You'll need to set up a server that can read the payload of the request and reply after the specified amount of time.
const xh = new XMLHttpRequest();
xh.open('GET', urlToYourServer, false);
xh.send('3');
where that '3' is the request body that the server parses (and responds after 3 seconds).
That said, you should not do this - it's a very inelegant and user-unfriendly approach. It'll stop any other action (including browser repainting and other requests) from occurring while this is going on. Better to properly wait for whatever you need (whether that's through a .then of a Promise, or a callback, or something else) - but without more context in the question, how exactly to accomplish this is unclear.
Assume the loading CSS class is attached to a div which contains an animated gif image (the classic spinner).
$( document ).ready(function(){
$(".button").click(function(event){
$(".loading").show();
// time-consuming code running in the browser
$(".loading").hide();
return false;
});
});
We have been accustomed to see the spinner image spinning while the client does an ajax request to the server, leaving control to the JavaScript engine which can animate the spinner until the ajax request's callback returns.
However, in the example above, there is no ajax involved and the "time-consuming code" is located on the client-side, meaning the JavaScript engine cannot animate the gif while doing some costly job on the client.
I am aware that there are no (native) threads in JavaScript, so how can I have that spinner shown and spinning while executing some JavaScript code?
If the situation is just as you say it is, this would be a rare scenario where it would make sense to put Web Workers to use. Chances are, you'd want to implement them in a way that provides a fallback that either displays a frozen GIF, or tries to do partial work based on timeouts, allowing the JavaScript call stack to complete occasionally so that it can do UI updating.
Compatible with IE10 and up, and pretty much everything else:
https://developer.mozilla.org/en-US/docs/Web/Guide/Performance/Using_web_workers
A random, incomplete example of timeout steps to give you an idea:
function doPartialWork() {
//// do one "step" of the complex logic.
// If complete, run a callback (and terminate without starting the timer)
if (complete) {
runCallback();
}
else {
// Start another slice of work on the next processing cycle
setTimeout(doPartialWork, 1);
}
}
Googlers: If the above link is broken, search for resources on "Web workers".
I've got an application that I intend to set a lock flag in my database that would exclude others from viewing that same page if set.
However, once set - I have no idea how to "unset" it. I could make it up to the user to unset the flag, but that seems unnecessary.
I'd want to simply look for the browser to leave the page, make a call to the database, and unlock the page.
How does one do this "type" (not looking for the exact way) of thing with JSF/Javascript/jQuery (all options)
There's really not a reliable way to do this, that I've seen anyway.
You can use the browser's onbeforeunload event to tell the server, "Hey I'm leaving the page now.". The issue is you can't actually block the page from unloading. If the user is actually closing the browser, any open sockets are going to be closed immediately. Your web server may or may not get the request in time. I've had very flaky results with this approach.
One approach that might work is to employ some sort of timeout mechanism. The page would ping the server every 30 seconds or what not, saying "I'm still here." If the server did not get this update after a few minutes, it would invalidate the session and free up that document. Perhaps this could be optimized by checking for the last ping when someone new came along. One issue with this is if someone left the page, the next user might have to wait a minute or two before they could go to the page. You'd then have to find a ping frequency that doesn't flood your server with traffic, but also doesn't make the next user have to wait too long.
It's also possible to combine these two methods. When the user leaves the page, trap the onbeforeunload event and immediately invalidate the session. However, if it didn't work, the session would time out after a minute of not being pinged.
Are there better solutions?
If you really need to lock a document in a web app so multiple users can't edit it, you might want to investigate your overall design. Are you afraid of users clobbering data? If so, maybe employ a mechanism that can resolve merge conflicts, or detect if both sets of changes can be combined.
If you wanted to go truly Web 2.0, you could design something similar to Google Docs, where changes appear live as they're made. No need for a Save button anywhere!
Sending a "keep-alive signal" might be an option. Something along these lines on the frontend side, combined with session cookies.
setInterval(function() {
var img = new Image();
var src = "http://examle.com/keepalive.gif?cachebuster=" +
Math.ceil(Math.random() * 10000 );
}, 1000);
From javascript I'm calling a web method. With IE, if I pass a huge parameter to that web method, an alert "Stop running this sciprt? A script on this page is causing internet explorer to run slowly" pops up.
Is it possible to handle the click on the "Yes" button, so that if the user decides to cancel the script execution I can run some alternative script (in this case, my "alternative" script consists in closing some progress bar I popup just before running the long time script).
I have seen many posts explaining how to prevent this alert from being displayed, but I don't want to stop the alert from being displayed: I just want to be able to handle the case in which the user decides to stop the script execution.
I've dealt with this before on an internal app where they didn't care how long it took the browser to crunch the numbers, they just didn't want to have to click the prompt.
The key is to break the work down into relatively predictable chunks of work (predictable in terms of CPU time) and run them on a setInterval like:
function doWork(begin, end) {
// Some chunk of what your worker function normally does from begin to end
if (actualEnd < end) // Nothing left to do
clearInterval(Interval);
}
var Interval = setInterval(doWork, 15);
This prevents the IE prompt from appearing (or Chrome from presenting the "Freeze" dialog). The next step is to add some code that lets the user skip it entirely; if the amount of work is known at the beginning, ask them right away. If not, start processing and after n chunks ask them if they'd like to do the cheaper function.
There are 2 other options for getting this much work done:
Look ahead as to how much work there is to be done, and if it's a lot, pass it to the server (this unfortunately means writing your JS again on the server; of course, you could use a server engine that runs Javascript to save yourself some coding).
Use background workers from Google Gears/HTML5
Finally, if you are doing this much work on demand, there are probably speed-up opportunities in the work you're performing - the data could be indexed beforehand on the server to make the calculations faster/simpler, things like that. But I don't know what you're calculating.