0

I am making a synchronous REST API call in the main thread. jQuery (or browser?) complains. But I don't know how to code it asynchronously, or at least the code is less confusing to read as it stands.

This is a simplified version of what I'm doing:

<html>
    <head>
        <script type="text/javascript" src="/js/jquery.min.js" ></script>
    </head>
    <body>
        <script type="text/javascript">
            function get_foobar(val1, val2){ 
                $.ajax({
                    url: 'http://localhost:8000/api/' + va1 + '/' + val2,
                    success: function (data) {
                       let my_object = $.parseJSON(data)
                       //console.log(data);
                       console.log(my_object);
                       let params = my_object['params'];
                       let blob = my_object['the_thing'];
                       var foobar = new FooBar(var1, var2, params, blob);

                       return foobar;  // <- object created here ...
                    },
                    async: false                    
                });

            }

            $(document.ready(function(){
                foobar = get_foobar($('#field1').val, $('#field2').val());
                console.log(foobar); // <- undefined here ... wtf?
            });
        </script>
    </body>
</html>

Why is the function not returning the foobar object that is created?

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
Homunculus Reticulli
  • 65,167
  • 81
  • 216
  • 341
  • remove `async: false` and the browser will stop complaining about sync ajax on the main thread. – Trevor Mar 14 '17 at 17:15
  • `I am making an asynchronous...` then you do `async: false` - is it simply you want to know how to do it? These contradict each other – StudioTime Mar 14 '17 at 17:17
  • 1
    @DarrenSweeney: well spotted. I actually meant to say SYNCHRONOUS call. I just noticed that and was about to change my title, when you beat me to it. – Homunculus Reticulli Mar 14 '17 at 17:18
  • [wait for async data](http://stackoverflow.com/questions/10004112/how-can-i-wait-for-set-of-asynchronous-callback-functions) – Gangadhar Jannu Mar 14 '17 at 17:19

4 Answers4

0

Solution on callback:

<html>
    <head>
        <script type="text/javascript" src="/js/jquery.min.js" ></script>
    </head>
    <body>
        <script type="text/javascript">
            function get_foobar(val1, val2, cb){ 
                $.ajax({
                    url: 'http://localhost:8000/api/' + va1 + '/' + val2,
                    success: function (data) {
                       let my_object = $.parseJSON(data)
                       //console.log(data);
                       console.log(my_object);
                       let params = my_object['params'];
                       let blob = my_object['the_thing'];
                       var foobar = new FooBar(var1, var2, params, blob);
                       cb(foobar);
                    },
                    //async: false//no reason to use it
                });

            }

            $(document.ready(function(){
                get_foobar($('#field1').val, $('#field2').val(), function(foobar){
                    console.log(foobar);
                });
            });
        </script>
    </body>
</html>
Pavlo Zhukov
  • 3,007
  • 3
  • 26
  • 43
0

There are a couple issues here.

  1. You are not returning anything in get_foobar.
  2. You cannot access the return value of the success callback.
  3. You should never use async: false

A common way to solve this is to use Promises

function get_foobar(val1, val2) {
    return new Promise(function(resolve, reject) {
        $.ajax({
            url: 'http://localhost:8000/api/' + va1 + '/' + val2,
            success: function (data) {
               let my_object = $.parseJSON(data)
               //console.log(data);
               console.log(my_object);
               let params = my_object['params'];
               let blob = my_object['the_thing'];
               var foobar = new FooBar(var1, var2, params, blob);

               resolve(foobar);  // <- resolve the object created here ...
            },
            error: function(err) {
                reject(err); // REJECT any errors that occur in request
            }              
        });
    });
}

get_foobar($('#field1').val, $('#field2').val())
    .then(function(foobar) {
        console.log(foobar)
    })
    .catch(function(error) {
        console.error('something went wrong!', error);
    });
Rob M.
  • 35,491
  • 6
  • 51
  • 50
  • But I want to make a **synchronus** call to the REST API.! The next line depends on the object having been returned from the preceeding function call. I suppose I could use a callback - but it makes the code unecessarily complicated ... :/ – Homunculus Reticulli Mar 14 '17 at 17:22
  • The idea is to move your code that depends on the returned data into your `then` callback. I don't think it is unnecessarily complicated to switch to using Promises, but I suppose that's a matter of opinion. Synchronous AJAX calls are deprecated as of jQuery 1.8 – Rob M. Mar 14 '17 at 17:25
  • Hmm, I'll have to refactor a hell of a lot of code - if I'm to use promises... I think it's a bit Draconian for JQuery to impose a particular style of programming on coders ... :( – Homunculus Reticulli Mar 14 '17 at 17:29
0

Here's an example that shows the problem you are facing, and the proper way to get data asynchronously (unfortunately, making Ajax request synchronously is bad practice and support for it will be removed from browsers in the future, so you definitely shouldn't rely on it).

The main problem is that by the time your asynchronous code runs, the function that called it has long since finished running and already returned. In this example, you're wanting a function like timer1 to return a result, but it returns it's result to the inner code of setTimeout, where it is actually called from. setTimeout returns nothing (hence undefined). What you need to do is use promises or for simple cases, callbacks. The second example in the code below uses callbacks to run additional code after the asynchronous operation.

I recommend you read this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop. It will help you understand asynchronous code in JavaScript, and why things work the way they work.

var log = (function(){
  var out = document.querySelector('#out');
  return function(msg){
    out.innerHTML += '<div>' + msg + '</div>';
  }
})();


function getSomethingAsyncTheWrongWay(){
  setTimeout(function timer1(){
    log('Round 1: First in code, last in console');
    return 'Result';
  }, 1000);
}


log(getSomethingAsyncTheWrongWay());

log('Round 1: Last in code, first in console');


function getSomethingAsyncTheRightWay(callback){
  setTimeout(function timer2(){
    log('Round 2: First in code, last in console');
    callback('Result');
  }, 1000);
}

getSomethingAsyncTheRightWay(function(result){
  log(result);
});

log('Round 2: Last in code, first in console');
#out{
  color:green;
  background:#000;
  font-family:courier new;
  padding:1em;
  font-weight:bold;
}
<div id=out></div>
Trevor
  • 13,085
  • 13
  • 76
  • 99
0

You won't be able to directly return 'blobby' to an in memory variable.

Pass 'blobby' to a global variable.

Try one of the following:

JQuery - Storing ajax response into global variable

Jquery - Store Ajax jSON response as variable

Community
  • 1
  • 1
NHorner
  • 26
  • 2
  • 4