1

My Question is almost identical to this one: How can I return a variable from a $.getJSON function

There are a number of other duplicates on Stack Overflow and I've read through them but yet to find a satisfactory answer.

I'm aware of the fact $.getJSON runs asynchronously, and I've been writing functions like:

function getSharePriceAtDate(date) {
    $.getJSON(queryString, function(data) {
        $("#share-price-at-date").html(data.values[0][date]);
    });
}

Which is fine, and lets me for example, write the data straight from a JSON source into a HTML document (using the callback to ensure the data has been received before it gets written out).

However, this really isn't what I want to do... I want to be able to call getSharePriceAtDate() from other parts of my code.

For example:

function calcChangeInValue(date) {
    var output = currentSharePrice - getSharePriceAtDate(date);
    return output;
}

For this I really need $.getJSON to run synchronously... I need to be able to create a getSharePriceAtDate(date) function that actually functionally does what it says and returns a specific value from a JSON source, rather than being forced to print to HTML within the callback.

The solution suggested in the question I linked was to rewrite $.getJSON() as the function it's shorthand for $.ajax() and set async = false.

This is more like what I want, but Chrome console gives the following warning:

Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check http://xhr.spec.whatwg.org/.

In my case, I am just grabbing 1 value, and having tested it in this instance, it really isn't that detrimental in any way I can see... (it just means my code will hang until the JSON request comes back, but it would have to anyway! asynchronous or otherwise - I still need that data for the calculation).

So I thought async = false must not be an issue in this case but the link in the warning above says:

Synchronous XMLHttpRequest outside of workers is in the process of being removed from the web platform as it has detrimental effects to the end user's experience. (This is a long process that takes many years.) Developers must not pass false for the async argument when the JavaScript global environment is a document environment. User agents are strongly encouraged to warn about such usage in developer tools and may experiment with throwing an InvalidAccessError exception when it occurs.

Which seems like it's saying if I use async = false my code won't be future proof.

Stack Overflow is full of people asking how to use $.getJSON() and the answers usually tell them to make use of the callback.

I'd like to know:

  1. What is the advantage of $.getJSON() being asynchronous in this context?
  2. How can I access external JSON data in a function that returns a value from the JSON? (i.e. How can I have something that lets me call var price = getSharePriceAtDate() and have my code wait while price is found)

EDIT: I already know how to do something like getSharePriceAtDate(calculateChangeInValue); the code I'm writing could really do with being able to print out share prices at given dates quite often, I was hoping for a better way

mike-source
  • 1,004
  • 3
  • 17
  • 32
  • 1
    Well, for one: the page won't be frozen, so your end user won't curse your ancestors :) – meskobalazs Jul 03 '15 at 11:34
  • In this case, since I'm doing a calculation on external data which is contained in the JSON, and the calculation can't be done without that data... then the end user would have to wait for the JSON anyway? If the JSON source has gone down or doesn't exist, I could write validation into my code to check for that. – mike-source Jul 03 '15 at 11:36
  • 1
    Yes, but e.g. if you have any other part of a page, which makes use of JavaScript, e.g. a dropdown menu, those won't work either. And that is something, which should be unacceptable. – meskobalazs Jul 03 '15 at 11:38
  • I think the answer given in the duplicate does cover all this, but I'm not sure its the best answer to my specific question. I think the answer to my Q2 is basically, no this can't be done. – mike-source Jul 03 '15 at 14:39

4 Answers4

2

Question 1: Async allows to you get data from the server without blocking your entire UI.

Question 2: Use a promise pattern

function getSharePriceAtDate(date) {
    //for example, if the price was contained in a JSON response: {price:$1200}
    return $.getJSON(date).then(function(data) { return data.price; });
}

function calcChangeInValue(date1,date2) {
  return $.when(getSharePriceAtDate(date2),getSharePriceAtDate(date1)
   .then(function(price2,price1) {
      return price2-price1;
   });

}

calcChangeInValue(date1,date2)
 .then(function(change) {
   $(element).html(change);
 });

And, for the record, this is also a duplicate of all those other questions on the site about how to return a value from an AJAX call.

Adam Jenkins
  • 51,445
  • 11
  • 72
  • 100
  • Thanks... didn't know anything about this way, it may indeed be a duplicate. But sometimes when you don't know what search terms to use you can get stuck finding very similar answers. A lot of what I was finding was people getting confused by `$.getJSON()` then being told to use the callback. – mike-source Jul 03 '15 at 11:53
0

You'll want to save the async responses somewhere and only do the calculations once both json files have arrived. But as Adam says, it's probably better to use promises if you can use them.

Shilly
  • 8,511
  • 1
  • 18
  • 24
0

All $.ajax in jquery returns a promise, or deferred. You can control with when() if all asynchronous tasks are completed and act in consecuence.

https://api.jquery.com/category/deferred-object/

https://api.jquery.com/promise/

https://api.jquery.com/jquery.when/

Marcos Pérez Gude
  • 21,869
  • 4
  • 38
  • 69
0

The short answer here is: No. You can't.

Longer answer: Look, this is the way Javascript works (and event-driven code in general, for that matter).

You shouldn't write purely linear code in an event-driven environment; it simply doesn't make sense and will lead to problems further down the line. Stop trying to work out how to do it your way, and learn to accept the way of doing things that the platform expects.

Don't try to pass the return value back; instead pass the rest of the code that will use that value into the initial call as an argument.

So you're hoping to write code that looks like this...

var output = calcChangeInValue(date1, date2);
//code that does something with output...

Instead, do this:

calcChangeInValue(date1, date2, function(output) {
    //code that does something with output.
});

The code that does something with output will run just the way you want it to -- ie immediately after the request returns. The difference is that the rest of your system will also continue working while the request is in progress.

Simba
  • 4,952
  • 3
  • 19
  • 29
  • Patronising (and missing the point). My question WAS how do I do it the way the platform expects. – mike-source Jul 03 '15 at 12:01
  • Also, this isn't really the solution, code would just continue to operate and the asynchronous JSON call would just come back at some point later (with the values I need). This was the issue I was having... sometimes there needs to be a way to force something to wait on the JSON coming back, the only way I could see to do that was to use $.getJSON()'s own callback, otherwise the calculations done will be wrong. – mike-source Jul 03 '15 at 12:04
  • @mike-source: His answer doesn't miss the point at all. This IS the way to do it. I suppose he could have shown your function invoking the callback, but I'm guessing he didn't want to come across as overly patronizing. Simba: Spot on. +1 –  Jul 03 '15 at 12:16
  • ...and your accepted answer is doing the same thing, just with a different way of handling the callback. –  Jul 03 '15 at 12:20
  • I don't see how this gives me a function that would return a value from an external JSON, I think you're focusing on the `calcChangeInValue()` function rather than `getSharePriceAtDate()`??? I may be wrong... slightly out of my depth. – mike-source Jul 03 '15 at 12:20
  • Please elaborate as I'd prefer to do it this way – mike-source Jul 03 '15 at 12:21
  • @mike-source: Let me put it this way, the answer you accepted doesn't return a value. It just uses an elaborate system to manage callbacks. The result is passed to the final callback, so no matter what, it's still the same asynchronous system at work. Any code that relies on the response needs to be in side the callback. –  Jul 03 '15 at 12:23
  • @mike-source: Here, I built a little system of handling any arbitrary number of requests/responses. The final callback will be given as many arguments as Dates that were passed to it, and in the same order. http://jsfiddle.net/au04n5ma/ –  Jul 03 '15 at 12:43
  • ...oops, forgot to negate the `--n` when I rearranged the code to make it variadic. http://jsfiddle.net/au04n5ma/1/ –  Jul 03 '15 at 12:58
  • Forgive my ignorance, but I'm lost at how you've called `calcChangeInValue(new Date(), new Date(), function(a, b) {})` in that example when it appears calcChangeInValue() takes no arguments? I didn't think I'd misunderstood so much of javascript :( – mike-source Jul 03 '15 at 13:28
  • and all of this is just an exercise in making my code easier to follow/maintain, I could just put my entire program into the $.getJSON() callback... – mike-source Jul 03 '15 at 13:30
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/82302/discussion-between-mike-source-and-squint). – mike-source Jul 03 '15 at 13:43
  • On investigation, I'm going with this as the closest answer, after discussion we ended up with this: http://jsfiddle.net/au04n5ma/6/ Which is how I originally had my program! So, no better way found... – mike-source Jul 03 '15 at 14:41