JavaScript callbacks and control flow - javascript

When are callbacks executed, for example the callback of a setTimeout() or a click event?
Do they pause code, that is already running, or do they wait until it has finished?
Example
I have a data structure (incrementalChanges) that records state changes caused by user interactions, for example mouse clicks. If I want to send all changes to another peer, I send him this data structure.
Another possibility is a full synchronisation (makeFullSync()), that means I send him my complete current state, so that I must empty the incremental changes (deleteIncrementalChanges()). That is, what you can see in the code. However I am not sure, what happens, if a user clicks something exactly between these two function calls. If this event fires immediately, then an item to the incrementalChanges structure would be added, but then in the second call directly deleted, so that it will never be sent and the other peer's state would became invalid.
makeFullSync();
/* what if between these 2 calls a new change is made, that is saved in the
changes data structure, that will be deleted by deleteIncrementalChanges()?
Then this change would be lost? If I change the order it is not better ...
*/
deleteIncrementalChanges();
Some good links and, in the case the first scenario (it pauses running code) is true, solutions are welcomed.

Javascript is single threaded, and keeps an event stack of stuff it needs to get to once it's done running the current code it's working on. It will not start the next event in the stack until the current one is finished.
If you make multiple asynchronous calls, such as calls for a server to update data on another client, you need to structure your code to handle the case where they don't necessarily reach the second client in the same order.
If you're sending changes one at a time to another user, you can time stamp the changes to track what order they were made on the first client.

Do they pause code, that is already running, or do they wait until it has finished?
They wait until it has finished. JavaScript is single threaded, more than one piece of code can not run at once. JS uses an event loop to handle asynchronous stuff. If an event such as a click handler or timer firing happens while another piece of code is running, that event is queued up and runs after the currently running code finishes executing.
Assuming makeFullSync(); and deleteIncrementalChanges(); are called in the same chunk of code they will be executed one after another without any click events being processed until after they have both run.
One almost exception to the nothing runs in parallel rule in JS is WebWorkers. You can send data off to a worker for processing which will happen in another thread. Even though they run in parallel their results are inserted back into the event loop like any other event.

Related

How can I update DOM while a method is running?

I'm writing a simple tool that works on the client side. Basically, the user brings in a file, presses a button to start it, it does stuff with it (takes about 10-15 seconds), and then it gives the modified file back.
Unfortunately, as soon as they press the button to start the method, the DOM doesn't update until the method is finished, so there's no feedback until it's all finished, which is quite frustrating.
In the template section, I have:
<p v-if="processingStatus==1">Processing data...</p>
The "processingStatus" variable is set to 0 by default.
In the method, which is "processData" and is called when the button is pressed, it starts with
this.processingStatus = 1
And then proceeds to process the data.
Unfortunately, the "Processing data..." tag doesn't show up until the method is finished. How can I force VueJS to render the DOM while the method is running?
Based on what you are saying that processData is doing (going through a bunch of data in arrays), my guess is that it is not running async and is locking the javascript thread until it is done.
What you need to do is inside processData is set the is processing variable like you are, but then do all the actual work inside a promise or some other mechanism to release control and update the UI.
Potentially you could also call
vm.$forceUpdate();
before starting all your array work.

Single thread Javascript and AJAX handling

Javascript is said to be single-threaded. Also AJAX is said to be asynchronous.
Consider a scenario;
I have a button and on click of it, I make a AJAX call which takes 5-6 seconds. Now the UI would not be blocked and the user does some other action (say click on another button which is now executing some code, while the AJAX response has been returned). Now in this case, since the other code is being executed, when would the AJAX callback be executed? Would it have to wait OR can it be executed in a parallel thread ?
The events are queued, so when the Ajax call completes, the handler for that would be queued to run on the event loop. When the single thread is done with your button handler, it'll then process the next event in the queue. So - you would have to wait for the code kicked off by the button click to finish, unless of course the Ajax request completed before the user clicked on the button, in which case the button click handler had to wait. The best you can do is split up your algorithm so that it runs in discrete chunks, these can be dropped onto the queue using setTimeout, but that is quite tricky.
So I have searched a little about this topic in general. Contrary to what I have imagined, javascript is nothing like multi-threaded. Instead, it has a queue of operations it performs.
The direct answer then is: Depending on the very exact timing, the AJAX callback might have to wait before click event completes. It also might have to wait for any other code that was executed at "the same moment".
This explains while things like while(true) or alert() stop every script on the site.

Delaying a setTimeout()

I'm having an issue with some asynchronous JavaScript code that fetches values from a database using ajax.
Essentially, what I'd like to do is refresh a page once a list has been populated. For that purpose, I tried inserting the following code into the function that populates the list:
var timer1;
timer1 = setTimeout([refresh function], 1000);
As you might imagine, this works fine when the list population takes less than 1 second, but causes issues when it takes longer. So I had the idea of inserting this code into the function called on the success of each ajax call:
clearTimeout(timer1);
timer1 = setTimeout([refresh function], 1000);
So in theory, every time a list element is fetched the timer should reset, meaning that the refresh function should only ever be called 1 second after the final list element is successfully retrieved. However, in execution all that happens is that timer1 is reset once, the first time the second block of code is reached.
Can anybody see what the problem might be? Or if there's a better way of doing this? Thanks.
==========
EDIT: To clear up how my ajax calls work: one of the issues with the code's structure is that the ajax calls are actually nested; the callback method of the original ajax call is itself another ajax call, whose callback method contains a database transaction (incorrect - see below). In addition, I have two such methods running simultaneously. What I need is a way to ensure that ALL calls at all levels have completed before refreshing the page. This is why I thought that giving both methods one timer, and resetting it every time one of the callback methods was called, would keep pushing its execution back until all threads were complete.
Quite honestly, the code is very involved-- around 140 lines including auxiliary methods-- and I don't think that posting it here is feasible. Sorry-- if no one can help without code, then perhaps I'll bite the bullet and try copying it here in a format that makes some kind of sense.
==========
EDIT2: Here's a general workflow of what the methods are trying to do. The function is a 'synchronisation' function, one that both sends data to and retrieves data from the server.
I. Function is called which retrieves items from the local database
i. Every time an item is fetched, it is sent to the server (ajax)
a. When the ajax calls back, the item is updated locally to reflect
its success/failure
II. A (separate) list of items is retrieved from the local database
i. Every time an item is fetched, an item matching that item's ID is fetched from the server (ajax)
a. On successful fetch from server, the items are compared
b. If the server-side item is more recent, the local item is updated
So the places I inserted the second code block above are in the 'i.' sections of each method, in other words, where the ajax should be calling back (repeatedly). I realize that I was in error in my comments above; there is actually never a nested ajax call, but rather a database transaction inside an ajax call inside a database transaction.
You're doing pretty well so far. The trick you want to use is to chain your events together, something like this:
function refresh()
{
invokeMyAjaxCall(param1, param2, param3, onSuccessCallback, onFailureCallback);
}
function onSuccessCallback()
{
// Update my objects here
// Once all the objects have been updated, trigger another ajax call
setTimeout(refresh, 1000);
}
function onFailureCallback()
{
// Notify the user that something failed
// Once you've dealt with the failures, trigger another call in 1 sec
setTimeout(refresh, 1000);
}
Now, the difficulty with this is: what happens if a call fails? Ideally, it sounds like you want to ensure that you are continually updating information from the server, and even if a temporary failure occurs you want to keep going.
I've assumed here that your AJAX library permits you to do a failure callback. However, I've seen some cases when libraries hang without either failing or succeeding. If necessary, you may need to use a separate set of logic to determine if the connection with the server has been interrupted and restart your callback sequence.
EDIT: I suspect that the problem you've got is a result of queueing the next call before the first call is done. Basically, you're setting up a race condition: can the first call finish before the next call is triggered? It may work most times, or it may work once, or it may work nearly all the time; but unless the setTimeout() is the very last statement in your "response-processing" code, this kind of race condition will always be a potential problem.

Dynamic page fetching with AJAX

i have a question regarding partial page loading with AJAX.
Suppose that an user clicks on a button that makes an AJAX call to load part of a page (it can possibly include dynamically loaded JS and/or CSS), and the html content is dropped on some div. Then, before the first load is complete he clicks on another button that makes another AJAX call that drops other content on the same div. How should i prevent this behaviour to create any conflicts? Some possible conflicts might be something like, for example, the first load executes some JS on content that is not found because the second load already changed that div.
Thanks in advance.
Edit:
I would appreciate answers based on asynchronous methods. Thanks.
Genesis and Gaurav are right about disabling user interaction. +1 from me to each of them. How you handle the logic is actually quite simple:
$('#my_submit_button').click(function(){
$.ajax({
url:'/my_file.php',
dataType='json',
beforeSend:function(){
$('#my_submit_button').prop('disabled',true);
},
error: function(jqXHR, status, error){
// handle status for each: "timeout", "error", "abort", and "parsererror"
// Show submit button again:
$('#my_ajax_container').html('Oops we had a hiccup: ' + status);
$('#my_submit_button').prop('disabled',false);
},
success:function(data){
$('#my_ajax_container').html(data);
$('#my_submit_button').prop('disabled',false);
}
});
});
make it synchronous (not recommended)
disable link/button while ajaxing
do not mind about it
but in your case it won't do any conflicts because when html is replaced, scripts too
Just disable the buttons that cause the AJAX calls to start while one has not completed yet.
I'm not sure this would actually be a problem for you because Javascript is single threaded. When the first ajax response comes in and you execute some javascript, that javascript cannot be interupted by the second ajax response as long as it is one continuous thread of execution (no timers or other asynchronous ajax calls as part of it's processing).
Let's run through a scenario:
User clicks button - first ajax call starts.
User clicks button - second ajax call starts.
First ajax call finishes and the completion code execution starts for what to do with the new data.
While code is executing from first ajax call, the second ajax call completes. At this point, the browser puts the second ajax call completion into a queue. It cannot trigger any of your completion code yet because the Javascript engine is still running from the first load.
Now the first load completes it's execution and code and returns from it's completion handler.
The browser now goes to it's queue and finds the next event to process. It finds the completion of the second ajax call and then starts the completion code for that ajax call.
As you can see from this scenario which has overlapping ajax calls and the second completing in the middle of the processing the first, there still is no conflict because the Javascript engine is single threaded.
Now, as the other answers have suggested, you make not want this user experience of launching a new request while one is still processing, but they won't technically conflict with each other. You have several tools you can choose from if you want to prevent overlapping calls:
You can prevent starting the second call while the first call is unfinished. You can do this both in the UI and in the actual code.
When there are multiple calls outstanding, you can decide to drop/ignore the earlier responses and not process them - waiting only for the last response.
When the second call is initiated, you can cancel the first call.
You can let the second just replace the first as in the above scenario.
The first two options require you to keep track of some cross ajax-call state so one ajax call can know whether there are others and act accordingly.

Any issue with setTimeout calling itself?

I have read a handful of setTimeout questions and none appear to relate to the question I have in mind. I know I could use setInterval() but it is not my prefered option.
I have a web app that could in theory be running a day or more without a page refresh and more than one check a minute. Am I likely to get tripped up if my function calls itself several hundred (or more) times using setInterval? Will I reach "stack overflow" for example?
If I use setInterval there is a remote possibility of two requests being run at the same time especially on slow connections (where a second one is raised before the first is finished). I know I could create flags to test if a request is already active, but I'm afraid of false positives.
My solution is to have a function call my jquery ajax code, do its thing, and then as part of ajaxComplete, it does a setInterval to call itself again in X seconds. This method also allows me to alter the duration between calls, so if my server is busy(slow), one reply can set a flag to increase the time between ajax calls.
Sample code of my idea...
function ServiceOrderSync()
{
// 1. Sync stage changes from client with the server
// 2. Sync new orders from server to this client
$.ajax( { "data": dataurl,
"success": function(data)
{ // process my data },
"complete": function(data)
{
// Queue the next sync
setTimeout( ServiceOrderSync, 15000 );
});
}
You won't get a stack overflow, since the call isn't truly recursive (I call it "pseudo-recursive")
JavaScript is an event driven language, and when you call setTimeout it just queues an event in the list of pending events, and code execution then just continues from where you are and the call stack gets completely unwound before the next event is pulled from that list.
p.s. I'd strongly recommend using Promises if you're using jQuery to handle async code.

Categories

Resources