1

I am having trouble using a JSON object outside of the AJAX success scope. I need to access the returned JSON object outside the AJAX success scope. I tried initializing a JS var outside the AJAX scope however and the assigning the JSON to it. However, this approach results in a catalog.item is undefined error. I appreciate any suggestions that will help me fix this problem.

This is what I tried:

This approach works perfectly fine (but not what I need):

            $('.catViewButton').click(function(){
                var overlay = jQuery('<div class="overlay"> </div>');
                overlay.appendTo(".invoice-body");
                $('.catalog-view-wrapper').show();
                $.ajax({
                    url: "ajax/invoice-get-data.php?loadCatalog=1",
                    dataType: "json",
                    success: function(catalog){
                        alert(catalog.item[0].image);
                         $('.catalog-img-container').attr("src", catalog.item[0].image);
                    }
                }); 

                        ...more code
                        .....
                        .....

This approach is what I need but results in an error:

        var catalog = [];
        $('.catViewButton').click(function(){
            var overlay = jQuery('<div class="overlay"> </div>');
            overlay.appendTo(".invoice-body");
            $('.catalog-view-wrapper').show();
            $.ajax({
                url: "ajax/invoice-get-data.php?loadCatalog=1",
                dataType: "json",
                success: function(cat){
                    catalog = cat;
                }
            }); 
            alert(catalog.item[0].image);
            $('.catalog-img-container').attr("src", catalog.item[0].image);
                    ...more code
                    .....
                    .....

Many thanks in advance!

AnchovyLegend
  • 12,139
  • 38
  • 147
  • 231
  • 1
    not a duplicate. programmer needs to understand the difference between making asynchronous and synchronous calls using jquery ajax – Akshay Khandelwal Sep 13 '13 at 18:33
  • 1
    @JamesMontagne - Felix sure is getting a lot of traffic on that one. – adeneo Sep 13 '13 at 18:33
  • 2
    @AkshayKhandelwal - It's an acronomym for "***Asynchronous*** Javascript and XML" for a reason ! – adeneo Sep 13 '13 at 18:34
  • I know but the need here is to utilize the data after reception and seems like there is nothing that he does meanwhile – Akshay Khandelwal Sep 13 '13 at 18:35
  • 1
    @AkshayKhandelwal your (deleted) answer wasn't wrong, but it fixes the problem in a way that will cause more issues down the road. The longer the OP goes without embracing the asyc nature of ajax the longer it will be before they start creating beautiful web apps. – Jason Sperske Sep 13 '13 at 18:36
  • @AkshayKhandelwal - `async:false` is almost never the solution... the solution is to use the correct code patterns when working with asynchronous code. – Steve Sep 13 '13 at 18:36
  • in that case you may need a function call where you utilize the returned data after being received – Akshay Khandelwal Sep 13 '13 at 18:37
  • Thanks for the replies everyone. What is the 'correct code pattern'? I'm sorry, I don't understand what I am doing wrong here and how to fix it, can someone please elaborate? The deleted answer sure did fix my problem, why was it downvoted? – AnchovyLegend Sep 13 '13 at 18:38
  • @AnchovyLegend - did you take a look at the link James provided above? The first answer to that question explains the problem and the correct patterns to use. – Steve Sep 13 '13 at 18:39
  • 1
    @AnchovyLegend you have to code your logic inside success handler or use methods exposed by promise interface returned by ajax method. Is it so hard to read the link posted by James Montagne, FIRST comment HERE – A. Wolff Sep 13 '13 at 18:40
  • Thanks for the reply. Yeah the link doesn't directly answer my question. It seems like a long and complicated answer to `How can I use the contents of a JSON object outside of the AJAX success callback?` :( – AnchovyLegend Sep 13 '13 at 18:42
  • 2
    This is an example of why sync is a bad idea. http://jsfiddle.net/NhKps/ Try typing your name in the input. It isn't very smooth/enjoyable is it? – Kevin B Sep 13 '13 at 18:42
  • 2
    Is it a long answer? Yes. Is it a complicated answer? Maybe. Does it answer your issue? Absolutely. So reading this long answer is paramount to understanding why you are running into the problem in the first place. – Steve Sep 13 '13 at 18:43
  • I am confused. Why is it that you need the object outside the success callback? – Akshay Khandelwal Sep 13 '13 at 18:44
  • Call your other method inside success, and pass your result as parameter. – sudhansu63 Sep 13 '13 at 18:45
  • 1
    @Akshay, Because there are events, later in my code, that use the data from the object. I suppose one approach would be to put these events inside the success callback... – AnchovyLegend Sep 13 '13 at 18:49
  • I think that's the whole idea explained by the link in first comment. Please go through it. It's the best solution for what you are trying – Akshay Khandelwal Sep 13 '13 at 18:51
  • Why is `async: false` so bad? – AnchovyLegend Sep 13 '13 at 18:57
  • Because `async: false` blocks all JS execution until your response comes back from the server. If you have a slow connection, the user can't do anything until the server responds. You might not notice this when testing locally/on a fast connection. But in the real world, it is at best annoying/sluggish and at worst unusable. Kevin B's example above is a perfect example. – Steve Sep 13 '13 at 19:00
  • I see. So would a reasonable (quicker performance) solution be to have a function call within the `success callback` that assigns the JSON object to the `catalog` variable? – AnchovyLegend Sep 13 '13 at 19:03

4 Answers4

3

You could use the Deferred Object which deals with asynchronous calls for you. Doing so your code will be executed in the reading order, no matter when the response is sent back. You can add as many callback as you need :

jqxhr = $.ajax({
    url: "ajax/invoice-get-data.php?loadCatalog=1",
    dataType: "json"
}); 
jqxhr.done(function(catalog) { 
    alert(catalog.item[0].image);
    $('.catalog-img-container').attr("src", catalog.item[0].image);
});
jqxhr.done(function(catalog) { 
    // use catalog again
});

The documentation is full of examples about all of that : http://api.jquery.com/jQuery.ajax/.

1

This is where the a of ajax is really important. Asynchronous means the alert and the subsequent .attr() lines are running before your success callback is run. You really should put these lines inside the success callback.

Jason Sperske
  • 29,816
  • 8
  • 73
  • 124
  • Thanks for the reply. But what if I need to use the contents of the JSON object outside the success callback? Is there a way around this? – AnchovyLegend Sep 13 '13 at 18:34
  • @AnchovyLegend Then you must still wait until after the success callback is executed. There is no way around that. Your code doesn't necessarily HAVE to be inside the success callback, but it will need to be in a function that gets executed either by the success callback or after the success callback. – Kevin B Sep 13 '13 at 18:36
  • @AnchovyLegend Yes, there is a way around this problem: see my answer. As several purists have pointed out, it's a kludge -- but it works. (Whether it is right for you depends on the volume of traffic your web app receives. Not every webapp is Google Mail... I use the method all the time, as do others who taught it to me.) – cssyphus Sep 13 '13 at 19:42
1

Look at deferred obj and .when(). You do not have to put everything in the success callback.

http://api.jquery.com/category/deferred-object/ http://api.jquery.com/jQuery.when/

Vlad
  • 978
  • 6
  • 13
0

This is how AJAX works. The results returned to the success: function are local to that function.

If you need to access them outside of the success function, one option is to store them in an element - such as a hidden <input> field, like this:

Supposing a hidden input field like this:

<input type="hidden" id="myHiddenField" />

Revised AJAX

$.ajax({
    url: "ajax/invoice-get-data.php?loadCatalog=1",
    dataType: "json",
    success: function(catalog){
        $('#myHiddenField').val(catalog);
        $('#myHiddenField').trigger('blur');
    }
}); 

Outside the AJAX function:

$('#myHiddenField').blur(function() {
    var cat = $(this).val();
    //now do something with the data in var cat
});
cssyphus
  • 37,875
  • 18
  • 96
  • 111
  • 1
    I downvoted because you modified the DOM to store JS data. You don't need to do that and it's not very efficient. – Vlad Sep 13 '13 at 18:43
  • How else would you do it? Occam's Razor: it's simple and it works. – cssyphus Sep 13 '13 at 18:44
  • 1
    Why would you need to store it in another element to do this? you could jus tas well have created an object, bound an event to that object, and then triggered an event there. Or better yet, used the built-in promise interface. – Kevin B Sep 13 '13 at 18:46
  • 2
    You can do the same with JS vars, you don't need the input. Sure it works with one and the impact on performance is not noticeable, but imagine if you did that for 1000 items. – Vlad Sep 13 '13 at 18:48
  • @gibberish: Please refer the first comment to the question about **How else would you do it?**. It's definitely not simple as it seems and works yes but causes performance issues. – Akshay Khandelwal Sep 13 '13 at 18:49
  • 2
    @KevinB AkshayKhandelwal Vlad - Would you mind posting your solution as an answer (or even adding onto my answer)? I didn't code my answer the way you suggest because I don't know what you know. *My answer does work... and it's posted...* – cssyphus Sep 13 '13 at 18:59
  • The problem I have with this approach is you are creating an async callback through the blur event which is the same as placing the code inside the success callback (but with an extra step of modifying the DOM). There is nothing wrong with setting a global var with a value inside an async callback, its just you have to wait for the async callback to execute before you use the value – Jason Sperske Sep 13 '13 at 22:08
  • @JasonSperske Perhaps I misunderstood the OP's underlying intentions, but he asked how to access the returned data outside the success callback -- which I presumed to mean at a later time. Suppose a user goes on to do various other things on that page, long after the AJAX has completed, and *now* that long-ago returned data is required. How can it be re-accessed at *that* point? – cssyphus Sep 14 '13 at 03:22
  • @gibberish, your approach works fine. It is not great performance wise (especially when dealing with 3,000+ items), but is exactly what I asked. I appreciate the reply. However, I ended up using Deferred Object. – AnchovyLegend Sep 14 '13 at 12:52
  • @AnchovyLegend Very good, thanks for letting me know. We are all on here to learn, myself more than most. Appreciate the feedback re performance. This question was very educational for me - thanks for raising it and moderating the responses. Good luck with the rest of your project. – cssyphus Sep 15 '13 at 15:01
  • and then use a json parser to aprse it again in form of json. Because now this cat is in string format and then try to reinitialize as cat = json.parse(cat); . Thank you buddy. – Ganpat Kakar Mar 03 '16 at 10:27