JavaScript synchronization and critical sections in event handlers - javascript

I have a function which is an event handler for websocket.onmessage, now since the server can send multiple messages (one after another) and each message will trigger that event, and since the function block may take a few seconds (a lot of rendering going on inside), the function may be called again while the first function call is still running.
I need a critical block in this function in some cases so that the second call will only start the critical section when the first call ends, what's considered a 'best practice' for implementing locks in JavaScript?

Since js is single-threaded, you can't really do locks. Well, you can but you shouldn't.
One idea might be to keep a status variable.
Your function will be called on each onmessage, but you only do something if the variable is set to false. If so, you set it to true and when its done, set it back to false.
var handler; //expose outside the closure
(function(){
var busy = false;
handler = function(){
if( !busy ){
busy = true;
//do rendering stuff
busy = false;
}
}
})();
Obviously, adapt this idea to your own needs.

You could use jQuery Socket https://github.com/flowersinthesand/jquery-socket as it has a callback for the message event.
message(data, [callback])
This means you can get the next message after the first has completed.
EXAMPLE:
websocket.onmessage(data, function(){
//get next message
});

JS is multithreaded only if you use webworkers. I don't know if websockets are even allowed on worker threads because of the lack of synchronization available, but if you set up all your websockets from the main thread, the events will all fire in order on the main thread, so you do not have to perform any blocking or synchronization yourself (see this thread on synchronization in JS)

Related

DOMContentLoaded event and task queue

I heard that there are three queues which have tasks in Event Loop Processing Model.
MacroTaskQueue : this queue have callback functions of setTimeout, setInterval ..etc
MicroTaskQueue : this queue have callback functions of promise, mutationOberver ..etc
AnimationFrameQueue : this queue have callback functions of requestAnimationFrame.
So, what i'm wondering is that
Who fires DOMContentLoaded event ?
Where the callback function of DOMContentLoaded is queued ? MacroTaskQueue or MicroTaskQueue?
finally,
var a = 10;
console.log(a);
setTimeout(function b() { console.log('im b'); }, 1000);
in this code,
var a = 10;
console.log(a);
is this code also queued in MacroTaskQueue or MicroTaskQueue ?
or only the b is queued in MacroTaskQueue after (min) 1000ms ?
Im in black hole. Help me please :D
What you call the "MacroTaskQueue" is actually made of several task-queues, where tasks are being queued. (Note that the specs only use multiple task-sources, there could actually be a single task-queue). At the beginning of the event-loop processing, the browser will choose from which task queue it will pick the next "main" task to execute. It's important to understand that these tasks may very well not imply any JavaScript execution at all, JS is only a small part of what a browser does.
The microtask-queue will be visited and emptied several times during a single event-loop iteration. For instance every time that the JS call stack has been emptied (i.e after almost every JS callback execution) and if it wasn't enough there are fixed "Perform a microtask checkpoint" points in the event-loop processing model.
While similar to a queue, the animation frame callbacks are actually stored in an ordered map, not in a queue per se, this allows to "queue" new callbacks from one of these callbacks without it being dequeued immediately after. More importantly, a lot of other callbacks are also executed at the same time, e.g the scroll events, resize events, Web animation steps + events, ResizeObserver callbacks, etc. But this "update the rendering" step happens only once in a while, generally at the monitor refresh rate.
But, that's not saying much about DOMContentLoaded.
Who fires DOMContentLoaded event ?
This event is fired as part of the Document parsing steps, in the "the end" section. The browser has to first queue a task on the DOM manipulation task-source. This task will then eventually get selected by the browser as part of the first step of the event-loop. And once this task's steps will be executed, the event will be fired and dispatched on the Document. That's only as part of this dispatch an event algorithm that the browser will invoke and inner-invoke until it calls our listener's callback.
Note that this Document parsing step is in itself quite interesting as a task since this is the most obvious place where you will have multiple microtask-checkpoints interleaved inside the "main" task (at each <script> for instance).
Where the callback function of DOMContentLoaded is queued ?
The callback function is not queued, it is conceptually stored in the EventTarget's event listener list. In the facts, it's stored in memory, since here the EventTarget is a DOM object (Document), it's probably attached to this DOM object, though this is an implementation detail on which the specs have little to say as this is transparent to us web-devs.
MacroTaskQueue or MicroTaskQueue?
As I hope you now understand better, neither. Task queues and the microtask-queue only store tasks and microtasks, not callbacks. The callbacks are stored elsewhere, depending on what kind of callbacks they are (e.g timers and events are stored in different "conceptual" places), and some task or microtask's steps will then call them.
is this code also queued in MacroTaskQueue or MicroTaskQueue?
That depends where this script has been parsed from. If it's inline in a classic <script> tag, then that would be the special parsing task we already talked about. If it's from a <script src="url.js">, then it will be part of a task queued from fetch a classic script, but it can also be part of a microtask, e.g if after an await in a module script, or you can even force it to be if you want:
queueMicrotask(() => {
console.log("in microtask");
eval(document.querySelector("[type=myscript]").textContent);
console.log("still in microtask");
});
console.log("in parsing task");
<script type="myscript">
var a = 10;
console.log(a);
setTimeout(function b() { console.log('im b'); }, 1000);
</script>
And it is even theoretically possible by specs that a microtask becomes a "macro-"task, though no browser does implements this anymore apparently.
All this to say, while I personally find all this stuff fascinating, as a web-dev you shouldn't block yourself on it.

Is using a while loop a good waiting strategy in a Firefox Restartless Extension?

I have a bootstrapped extension which interacts with the chrome part of Firefox (i.e. even before the content loads), and needs to query an SQLite database for some check. I would prefer a sync call. But, since a sync call is bad in terms of performance and can cause possible UI issues, I need to make an async DB call.
My use case is such:
Make aysnc call to database
Once completed do further processing
Now, this can be easily handled by placing 'further processing' part in handleCompletion part of executeAsync function.
But, I want the 'further processing' to be done irrespective of this statement being executed i.e. This DB lookup may or may not happen. If it doesn't happen well and good, go ahead. If it does I need to wait.
So, I am using a flag based strategy; I set a flag handleCompletionCalled in handleError & handleCompletion callback to true.
In the further processing part, I do a
while(handleCompletionCalled) {
// do nothing
}
//further processing
Is this a good strategy or can I do something better ( I don't really want to use Observers, etc. for this since I have many such cases in my entire extension and my code will be filled with Observers)?
Using a while loop to wait is a seriously Bad Idea™. If you do, the result will be that you hang the UI, or, at a minimum, drive CPU usage through the roof by rapidly running though your loop a large number of times as fast as possible.1
The point about asynchronous programming is that you start an action and then another function, a callback, is executed once the activity is completed, or fails. This either allows you to start multiple actions, or to relinquish processing to some other part of the overall code. In general, this callback should handle all activity that is dependent on the completion of the asynchronous action. The callback function, itself, does not have to include the code to do the other processing. After it has done what needs to happen in response to the async action completing, it can just call another function like doOtherProcessing().
If you launch multiple asynchronous, actions you can then wait for the completion of all of them by having flags for each task and a single function that is called at the end of all the different callback functions like:
function continueAfterAllDone(){
if(task1Done && task2Done && task3Done && task4Done) {
//do more processing
}else{
//Not done with everything, yet.
return;
}
}
This could be extended to an arbitrary number of tasks by using an array, or task queue, which the function then checks to see if all of those are completed rather than a hard-coded set of tasks.
Waiting:
If you are going to have another processing path which executes, but then must wait for the completion of the asynchronous action(s), you should have the wait performed by setting up a timer, or interval. You then yield the processor for a specified period of time until you check again to see if the conditions you need to proceed have occurred.
In a bootstrapped add-on, you will probably need to use the nsITimer interface to implement a timeout or interval timer. This is needed because at the time you are running your initialization code it is possible that no <window> exists (i.e. there may be no possibility to have access to a window.setTimeout()).
If you are going to implement a wait for some other task, you could do it something like:
const Cc = Components.classes;
const Ci = Components.interfaces;
var asyncTaskIsDone = false;
var otherProcessingDone = false;
// Define the timer here in case we want to cancel it somewhere else.
var taskTimeoutTimer;
function doStuffSpecificToResultsOfAsyncAction(){
//Do the other things specific to the Async action callback.
asyncTaskIsDone = true;
//Can either call doStuffAfterOtherTaskCompletesOrInterval() here,
// or wait for the timer to fire.
doStuffAfterBothAsyncAndOtherTaskCompletesOrInterval();
}
function doStuffAfterBothAsyncAndOtherTaskCompletesOrInterval(){
if(asyncTaskIsDone && otherProcessingDone){
if(typeof taskTimeoutTimer.cancel === "function") {
taskTimeoutTimer.cancel();
}
//The task is done
}else{
//Tasks not done.
if(taskTimeoutTimer){
//The timer expired. Choose to either continue without one of the tasks
// being done, or set the timer again.
}
//}else{ //Use else if you don't want to keep waiting.
taskTimeoutTimer = setTimer(doStuffAfterBothAsyncAndOtherTaskCompletesOrInterval
,5000,false)
//}
}
}
function setTimer(callback,delay,isInterval){
//Set up the timeout (.TYPE_ONE_SHOT) or interval (.TYPE_REPEATING_SLACK).
let type = Ci.nsITimer.TYPE_ONE_SHOT
if(isInterval){
type = Ci.nsITimer.TYPE_REPEATING_SLACK
}
let timerCallback = {
notify: function notify() {
callback();
}
}
var timer = Cc["#mozilla.org/timer;1"].createInstance(Ci.nsITimer);
timer.initWithCallback(timerCallback,delay,type);
return timer;
}
function main(){
//Launch whatever the asynchronous action is that you are doing.
//The callback for that action is doStuffSpecificToResultsOfAsyncAction().
//Do 'other processing' which can be done without results from async task here.
otherProcessingDone = true;
doStuffAfterBothAsyncAndOtherTaskCompletesOrInterval();
}
Initialization code at Firefox startup:
The above code is modified from what I use for delaying some startup actions which do not have to be done prior to the Firefox UI being displayed.
In one of my add-ons, I have a reasonable amount of processing which should be done, but which is not absolutely necessary for the Firefox UI to be shown to the user. [See "Performance best practices in extensions".] Thus, in order to not delay the UI, I use a timer and a callback which is executed 5 seconds after Firefox has started. This allows the Firefox UI to feel more responsive to the user. The code for that is:
const Cc = Components.classes;
const Ci = Components.interfaces;
// Define the timer here in case we want to cancel it somewhere else.
var startupLaterTimer = Cc["#mozilla.org/timer;1"].createInstance(Ci.nsITimer);
function startupLater(){
//Tasks that should be done at startup, but which do not _NEED_ to be
// done prior to the Firefox UI being shown to the user.
}
function mainStartup(){
let timerCallback = {
notify: function notify() {
startupLater();
}
}
startupLaterTimer = startupLaterTimer.initWithCallback(timerCallback,5000
,Ci.nsITimer.TYPE_ONE_SHOT);
}
Note that what is done in startupLater() does not, necessarily, include everything that is needed prior to the ad-on being activated by the user for the first time. In my case, it is everything which must be done prior to the user pressing the add-on's UI button, or invoking it via the context menu. The timeout could/should be longer (e.g. 10s), but is 5s so I don't have to wait so long for testing while in development. Note that there are also one-time/startup tasks that can/should be done only after the user has pressed the add-on's UI button.
1. A general programming issue here: In some programming languages, if you never yield the processor from your main code, your callback may never be called. In such case, you will just lock-up in the while loop and never exit.

What happens after a synchronous event is handled?

Given the single-threadedness of javascript, when a synchronous event is fired (for DOM manipulation for example) what happens to the currently executing function block when it is interrupted? How does the browser know where/when to continue execution of the interrupted block? Is there some sort of internal addition of a "pointer" to the event loop? I ask because I'm curious whether other waiting events in the event loop can intervene between the intervention of the synchronous event handler and the continuation of the original function block being executed.
And I am very early in my understanding of asynch events/synchronous events/event loop so if I have this totally wrong please let me know. But my understanding is that synchronous events (nested?) are "fired immediately" and I have seen it happen on jsfiddle with standard cut and paste from tutorials on the subject. I'm just confused as to how javascript knows how/where to pick up where it left off since it is so asynch driven.
A snippet:
<script>
var button = document.body.children[0]
var text = document.body.children[1]
button.onclick = function() {
alert('in onclick')
text.focus()
alert('out onclick')
}
text.onfocus = function() {
alert('onfocus')
text.onfocus = null //(*)
}
</script>
will produce 'in onclick', 'onfocus', 'out onclick'. The focus is a synchronous event. How does js know to pick back up with next statement after "text.focus()"? Is it something as simple as the something being done with the frame stack?
All the built-in events are asynchronous, but if you were to fire a synchronous event in your code, it would work like any regular function call that returns back once it's done. No event suddenly interrupts executing code - well pressing "stop execution" in developer tools actually does because the compiled code is littered with checks for that in every function and loop.

Callbacks overlapping

I have a simple resizable div (achieved with jQuery's resizable) and I'm handling the resize event with a function.
So, let's say that's my code:
var a = ...
$(div).resizable({
options go here...
resize: function(e, ui){
make some operations with a
...
...
keep making operations with a
...
...
ok, we're done
}
})
Now, let's say the resize callback is called a lot of times in a short period of time. How will javascript handle that? Will callbacks overlap the use of "a"? (Note that "a" is a global var!).
Am I safe with this code or there might be a conflict of some kind?
Regards
If you're asking about thread safety, JavaScript is single-threaded. A function like that can only be run one at a time, regardless of how often it's called.
So you should be safe.
JavaScript is single-threaded so the function calls can't "overlap". If you trigger the event multiple times then the function is executed entirely (minus any asynchronous functions it may call) in response to the first triggering, before it's executed again to handle the subsequent triggers.
However, changes to a in one call will still have been made when the function is executed again, so if it relies on the value of a you may run into problems - it comes down to exactly what that function is doing with the variable.

Checking if a JavaScript setTimeout has fired

I'd like to be able to dispatch a bunch of work via JavaScript to be done in the browser in such a way that the browser stays responsive throughout.
The approach I'm trying to take is to chunk up the work, passing each chunk to a function that is then queued with a setTimeout(func, 0) call.
I need to know when all the work is done, so I'm storing the returned timer ID in a map (id -> true|false). This mapping is set to false in the next block of code after I have the timer ID, and the queued function sets the mapping to true when it completes... except, of course, the queued function doesn't know its timer ID.
Maybe there's a better/easier way... or some advice on how I can manipulate my map as I need to?
I would queue the work in an array, use one timeout to process the queue and call a callback once the queue is empty. Something like:
var work = [...];
var run = function(work, callback) {
setTimeout(function() {
if(work.length > 0) {
process(work.shift());
setTimeout(arguments.callee, 25);
}
else {
callback();
}
}, 25);
};
run(work, function() {
alert('Work is done!');
});
As JavaScript in browsers is single threaded there is no real advantage to run multiple timeouts (at least I think this is what you are doing). It may even slow down the browser.
I'd like to add that although javascript is single threaded you can still have multiple ajax calls going at once. I recently had a site that needed to do potentially hundreds of ajax calls and the browser just couldn't handle it. I created a queue that used setTimeOut to run 5 calls at once. When one of the ajax calls returned it fired a callback (which is handled by a single thread) and then made the next call on the stack.
Imagine you're a manager that can only talk to one person at a time, you give 5 employees assignments, then wait for their responses, which may come in any order. Once the first employee comes back and gives you the information, you give them a new assignment and wait for the next employee (or perhaps even the same employee) to come back. So although you're "single threaded" 5 things are going on at once.
There is an example right in the HTML Standard, how it is best to handle it:
To run tasks of several milliseconds back to back without any delay,
while still yielding back to the browser to avoid starving the user
interface (and to avoid the browser killing the script for hogging the
CPU), simply queue the next timer before performing work:
function doExpensiveWork() {
var done = false;
// ...
// this part of the function takes up to five milliseconds
// set done to true if we're done
// ...
return done;
}
function rescheduleWork() {
var handle = setTimeout(rescheduleWork, 0); // preschedule next iteration
if (doExpensiveWork())
clearTimeout(handle); // clear the timeout if we don't need it
}
function scheduleWork() {
setTimeout(rescheduleWork, 0);
}
scheduleWork(); // queues a task to do lots of work
The moment of finishing the work is pretty clear, when clearTimeout is called.

Categories

Resources