1

I'm developping a local (no server, no ajax) piece of code that does "heavy" computing and visual representations. My ever growing problem is thus : pieces of code will constantly be executed before preceding lines of code have finished! This is killing me and I've read, read and read about callbacks, promises, but unless I'm thick headed it doesn't seem to apply to my context and I can't wrap my head around how Javascript's flow of execution works.

If I call 3 functions from within a .js file loaded in the head of my html file like thus :

FirstFunction(lenghtOfDataset); // creates a complex array of arrays of objects (about 10,000 elements)
SecondFunction (Dataset); // renders a D3.js svg visualization based on the dataset (about 1,000 elements)
ThirdFunction (Dataset); // creates an html table by using jQuery .append (bad, I know) using the same dataset (around 10,000 elements)

Now, why, oh why does code in the third function is executed before the first function has even finished executing? (Of course, resulting in an "undefined" error)

Another example which drives me crazy goes like this :

$("#my-table").append("<thead><tr>");
for (i=FirstNumber; i<=LastNumber; i++) { // about 1000 elements
    $("#my-table").append("<th>" + i + "</th>");
    }   
$("#my-table").append("</tr></thead>");

Again, why, oh why does the closing "</tr></thead>" get inserted before the for loop is even finished?!

EDIT : OK this example has invalid code, thanks to mplungjan for providing a solution, but this is not the main issue. Consider this code then :

$("#working-overlay").css("display", "block");
for (var i = 1; i <= 10000; i++) {              
        Number[i] = {};
        Number[i].value = i;
        Number[i].divisors = DivisorsOf(i); // I have a function that calculates divisors   
        Number[i].factors = FactorsOf(i); //// I have a function that calculates factors
}
$("#working-overlay").css("display", "none");

The working-overlay will get display:none before computation is finished. How to go about not behaving that way? I don't really care about the overlay here, it's an example, but I do care that the next computation in line will refer to an undefined value because the for loop isn't really finished when the next line of code is executed.


Please note that I'm not looking for a copy-and-paste solution nor a workaround in the setTimeout style (works inconsistently, anyway), but I want to understand why it behaves like so: why does Javascript code execution flows, weirdly, ahead of itself?

Steph
  • 23
  • 4
  • This isn't about Javascript; it's about jQuery. jQuery follows a *functional* design pattern, and executes things *asynchronously.* It shouldn't matter, nor should you care, in what order it executes things. You'd better get used to it, because asynchrony is at the hear of the way all browsers work. – Robert Harvey Oct 19 '16 at 14:54
  • @RobertHarvey I just accidentally deleted my comment. The link I had referenced earlier that I believe you may get some additional information out of is here: [Synchronous vs Asynchronous](http://stackoverflow.com/questions/748175/asynchronous-vs-synchronous-execution-what-does-it-really-mean). – Tyler Roper Oct 19 '16 at 14:55
  • @RobertHarvey well well, there is a time where order matters, if not, programming would have been impossible, even in functional style. – Jean-Baptiste Yunès Oct 19 '16 at 14:56
  • @Jean-BaptisteYunès: Yes, but not jQuery. You can't bend jQuery to your will this way. If you want it to be executed in a particular order, you have to use plain old Javascript or learn continuation passing style. – Robert Harvey Oct 19 '16 at 14:57
  • Something's a-miss here. The code you've shown should not behave the way you've described. --- Without seeing more... it seems like one of your functions starts an asynchronous request, which then lets the following code execute. But where is that asynchronous code happening? – Don Cheadle Oct 19 '16 at 15:01
  • 1
    You cannot append partial html in jQuery. What you see is jQuery trying to FIX your illegal HTML - here is how you could do it correctly: https://jsfiddle.net/mplungjan/k4xzo3yu/ – mplungjan Oct 19 '16 at 15:03
  • @RobertHarvey There is probably some misunderstanding. The for loop in his second example is not asynchronous (it is pure javascript isn't it?), but instructions in it are asynchronous, this means that it can be possible to see results of the following append before inner appends finished, but execution of the code that follows the for loop is executed after the for loop itself. – Jean-Baptiste Yunès Oct 19 '16 at 15:04
  • @Jean-BaptisteYunès: It's got jQuery in it, so all bets are off. – Robert Harvey Oct 19 '16 at 15:05
  • @RobertHarvey No bet! Instructions are executed in sequence, but as they contain asynchronous calls their effects can be disordered. – Jean-Baptiste Yunès Oct 19 '16 at 15:06
  • @Jean-BaptisteYunès - where is the asynchronous calls? $().append() shouldn't be asynchronous. – Don Cheadle Oct 19 '16 at 15:09
  • 1
    It is not. His code is invalid – mplungjan Oct 19 '16 at 15:11
  • @mmcrae You are right, it seems that it is the DOM updating that is asynchronous. – Jean-Baptiste Yunès Oct 19 '16 at 15:14
  • @Jean-BaptisteYunès No, the problem is that when you insert a plain `` tag without a closing `
    ` tag, you've got invalid markup and so the browser will (essentially) ignore it. It has nothing to do with synchronous/asynchronous behavior. The DOM has to make sense after each individual call to APIs like `.append()`.
    – Pointy Oct 19 '16 at 15:50
  • Thank you all for your comments, but I'm nowhere nearer understanding. The link provided by @Santi is interesting, but doesn't help me understand how to *not* start an asynchronous request. I kind of figured out - thus my question - that "asynchrony is at the heart of all browsers", but I still need A and B to execute before C! – Steph Oct 19 '16 at 16:11
  • @RobertHarvey "It shouldn't matter, nor should you care, in what order it executes things." Wrong. I do matter and it should! I'm not building a webpage, but a sort of scientific local calculating piece of code, so it does matter very much. – Steph Oct 19 '16 at 16:12
  • Then you're going to have to use traditional Javascript or learn continuation-passing style. jQuery doesn't guarantee an order of execution without these considerations. However, I'd fix the obvious problems in your code first, like the missing tags. – Robert Harvey Oct 19 '16 at 16:12
  • @RobertHarvey I've edited my question. The missing tags aren't the issue. So what you're saying is that jQuery is asynchronous, but if I did things like selectelementbyid it would become synchronous? Haven't I read at many places that Javascript is "functional programming" (which you attribute to jQuery)? "Traditional" Javascript? You're loosing me. – Steph Oct 19 '16 at 16:29
  • jQuery is not async. It however supports async very well. Without seeing your functions we cannot guess what is happening. Please create a complete example using the `<>` snippet editor – mplungjan Oct 19 '16 at 16:41
  • How do you show/know the overlay is hidden before the computation is done? – mplungjan Oct 19 '16 at 16:44
  • I know because as stated in the first part of the question, the first function (which part of it is shown in my "edit" second example) still isn't finished when the other functions starts executing. The overlay is but a simplification to illustrate the problem. – Steph Oct 19 '16 at 18:07
  • @Pointy ok thanks, I understand. – Jean-Baptiste Yunès Oct 19 '16 at 18:08

1 Answers1

1

Your first example is likely an issue of asynchronous handling js D3.js as we mentioned in our comments

Your second example is invalid. You cannot use append to insert partial HTML

Here is a valid way:

var $tHead = $("#myHead"),
  $headerRow = $("<tr/>");
for (var i = 0; i <= 1000; i++) { // about 1000 elements
  $headerRow.append("<th>" + i + "</th>");
}
$tHead.append($headerRow);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<table id="my-table">
  <thead id="myHead"></thead>
</table>

Likely faster way

var $tHead = $("#myHead"),
  headerRow = "<tr>";
for (var i = 0; i <= 1000; i++) { // about 1000 elements
  headerRow+="<th>" + i + "</th>";
}
headerRow+="</tr>";
$tHead.append(headerRow);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<table id="my-table">
  <thead id="myHead"></thead>
</table>
mplungjan
  • 169,008
  • 28
  • 173
  • 236
  • Thank you for correcting my invalid code! It will be useful for sure, but that still doesn't answer my existential question. – Steph Oct 19 '16 at 16:02
  • The d3 is likely using Async execution - see posted link to other answer - JS is single threaded unless execution is deferred in async callbacks – mplungjan Oct 19 '16 at 16:09
  • So if I understand this right D3 and jQuery use async execution, so I would have to somehow write Javascript without using any libraries/tools in order for my code to be sync execution? Sounds like a nightmare ;) – Steph Oct 19 '16 at 16:33
  • No you do not. You need to understand where they are using it. The append you did was not async. It just had a side effect of closing your tr before appending - you need to show us your code and we can help you execute the functions in the correct place. – mplungjan Oct 19 '16 at 16:36
  • I understand about "showing my code", but its pages and pages long, lots of different files and written in French - not that language should really matter, though. The question then becomes : how do I know what (in Javascript) is asynch and what is not? Any suggested reading? – Steph Oct 19 '16 at 18:04
  • As for "showing the code", would an online version of the whole project be enough? I could do that, but I'm not sure it is relevant, there would be a lot to look around rather than just understanding what makes B and C start executing before A does (the 3 functions example in my question)... – Steph Oct 19 '16 at 18:09
  • For example d3 transition the statement after calling it will be executed right away. If you want something to happen after the transition is done, you need to put the next statement in the callback – mplungjan Oct 19 '16 at 18:11
  • Here is your owl example in visible terms. this is how you could have shown the issue - I added a setTimeout and a slow to show you how to keep stuff visible longer https://jsfiddle.net/mplungjan/9cuLh2gL/ – mplungjan Oct 20 '16 at 05:54
  • I don't mean to be rude, but I did mention in my question that I didn't want workarounds in the likes of setTimeout. – Steph Oct 21 '16 at 22:32
  • It was not meant as a workaround - Your examples are not very user-friendly. - why hIde after processing. Try something that is easier to see the result – mplungjan Oct 22 '16 at 05:55
  • Here is a version that shows that the calculation is DONE before the jQUery starts fading out: https://jsfiddle.net/qm4wef1v/1/ – mplungjan Oct 22 '16 at 06:21
  • In your Fiddle, crank the for loop to 50000 and put the fadeout at 2000 and you'll start to see what I mean. The bigger the calculations, weirdly, the more pieces of code supposedly done after do happen meanwhile or before the end of the calculations. In my project, it sometimes goes as much as 1 million for the for loop. – Steph Oct 24 '16 at 21:34
  • So I did what you said, And it came out with the result and it faded quite quickly. That is not because jQuery does not wait, that is because the screen cannot throw that much data on the screen before the next statement is executed. It is not JS that is the culprit but your screen buffer and browser. – mplungjan Oct 25 '16 at 05:28
  • 1
    Interesting, thanx. Screen buffer and browser. That explains why I consistently need to put a setTimeout(function Heavy_Calculations,300) after showing a "waiting overlay" otherwise the overlay doesn't have time to show. But it still doesn't explain why a var MyVar = 345 in the first function still don't get through before the third function executes (the example in my question). I'll try to setup a fiddle of this behavior. – Steph Oct 25 '16 at 23:10