Monday, June 4, 2012

What happens in JavaScript when an AJAX call returns while the script is executing?


Suppose I write some JavaScript that performs an AJAX call with myCallback as a callback method to execute when the AJAX succeeds.



Suppose then that some other JavaScript method called myFunction is being invoked on my page when myCallback is invoked asynchronously.



Does one operation take precedence over the other? Do they both run at the same time? What happens?


Source: Tips4all

8 comments:

  1. Suppose then that some other JavaScript method called myFunction is being invoked on my page when myCallback is invoked asynchronously.

    Does one operation take precedence over the other? Do they both run at the same time? What happens?


    JavaScript on browsers is single-threaded (barring your using web workers, and the syntax for that is explicit). So myFunction will run until it returns — with certain caveats (keep reading). If the ajax layer completes an operation while myFunction is running (which it very well may) and needs to invoke the callback, that call gets queued. The next time your code yields, the next call in the queue will be triggered.

    It might seem, then, that we never have to worry about race conditions. That's mostly true, but there are subtleties. For instance, consider this code:

    var img = document.createElement('img');
    img.src = /* ...the URL of the image... */;
    img.onload = function() {
    // Handle the fact the image loaded
    foo();
    };
    doSomethingElse();
    doYetAnotherThing();


    Since JavaScript on browsers is single-threaded, I'm guaranteed to get the load event when the image loads, right?

    Wrong.

    The JavaScript code is single-threaded, but the rest of the environment probably isn't. So it can happen that, having set the img.src, the browser may see that it has a cached copy of the image it can use, and so it triggers the load event on the img between the img.src = ... line and the img.onload = ... line. Since my handler isn't attached yet, I don't get the call, because by the time I've attached my handler, the event has already fired.

    But you can see the effect of queuing if we reverse those lines:

    var img = document.createElement('img');
    img.onload = function() {
    // Handle the fact the image loaded
    foo();
    };
    img.src = /* ...the URL of the image... */;
    doSomethingElse();
    doYetAnotherThing();


    Now I'm hooking the event before setting src. If the event fires between the img.src = ... line and the doSomethingElse line (because the browser has the image in cache), the callback to my handler gets queued. doSomethingElse and doYetAnotherThing run before my handler does. Only when control passes out of my code does the queued call to my handler finally get run. The JavaScript code is single-threaded, but the environment is not.

    You can also yield to the host environment in non-obvious ways. For instance, by calling alert or its breathren confirm, prompt, etc. These functions stick out like the sore thumbs they are in modern JavaScript because they aren't event driven; instead, JavaScript execution is suspended while a modal window is shown. But as bobince points out in his in-depth discussion here, that doesn't mean none of your other code will run while that modal is showing. It's still single-threaded, but the one thread is being suspended in one place (by the modal) and used to run code elsewhere in the meantime; a very fine distinction indeed. (Bob also points to some event handling — his focus example — that seems to break this rule, but it doesn't. His example calls focus, which in turn calls the event handlers, and then returns; no different from you calling your own functions.) The key thing the items that Bob points out have in common is that your code has called into something in the host environment that does goes away and does something (shows a modal dialog, fires blur and focus handlers, etc.).

    (alert and its breathren in particular cause all sorts of nastiness, particularly around focus and blur, and I recommend avoiding them in favor of more modern techniques (which can also look about 18x better).)

    So those are the caveats mentioned at the outset of the answer. And in particular, if myFunction calls alert, at least on Firefox the ajax completion callback will get run during the alert (it won't on most other browsers). If you're curious to try out what does and doesn't happen during alerts and such, here's a test page testing setTimeout and ajax; you could extend the tests to go further.

    ReplyDelete
  2. These answers are all wrong [edit: at least 3-4 wrong when this answer was posted]. The XHR event is put in the event queue/pool. When the single-threaded javascript thread is not busy, it will grab the next events from the event pool, and act on them. One of those events will be your XHR "AJAX" request, which will trigger the callback, which will then execute. This is how all event-driven systems without interrupts work.

    edit: Upvoting Joe's answer which links to Is javascript guaranteed to be single-threaded? and mentions the subtle edge-cases. Very informative.

    ReplyDelete
  3. Javascript kind of single threaded see Is javascript guaranteed to be single-threaded?. So the answer is NO, your event handler and your function could run at the same time, or they might not.

    ReplyDelete
  4. The AJAX callback runs on the next event loop.

    ReplyDelete
  5. The ajax calls with be simultaneously. So when the ajax call is successful, the callback function is invoked which in turn invokes the the myfunction.

    ReplyDelete
  6. You could quickly test this by putting an alert('first one'); alert('second one'); in both functions, and see which modal popup comes up first. If your code really is affected by the order, put some conditional checks in both to prevent / allow something. The speed of native js vs the speed of a full round-trip to and from the server is wildly different. Unless you have some javascript running in an infinite loop, the odds of the ajax call returning during your other function execution is tiny.

    ReplyDelete
  7. AJAX calls are "asyncronous" which means "fire off a call and let me know what you want me to call back when im done".

    so in short:

    function DoSomething() { AJAXCall(Callback); }

    ... some time passes then ...

    function Callback() { Done(); }


    Between those 2 function calls anything can happen, the callback will still be called when the response comes back because although the javascript runtime is single threaded the browser will stack calls to happen in some sort of order (likely the order they are made but not garanteed).

    It's a shame i just pulled done my old website because i had classic example of a page making several ajax calls on load, as each call came back it would populate various portions of the page.

    although each call was "attached" to the document loaded event only 1 call could be made at a time but the server could respond to any call in any order because the server could be multithreaded (and likely is).

    Think of javascript as being a virtual box in which which the browser can pass a small lump of code, it can only run 1 lump at a time but it can run lots of scripts in whatever order it sees fit (generally this is unpredictable with multiple ajax calls because who knows when the server might come back with a result)

    ReplyDelete
  8. When the ajax succeeds and invokes myCallback, it will run synchronously. So myCallback is like other functions, you call it first it will run first or you call it last it will run last. THEY WILL NOT RUN AT THE SAME TIME.

    ReplyDelete