0

This is something that should be dead easy, but I can't figure it out for the life of me, and can't find anything online to really help me.

I have a list ['flower', 'grass', 'rock', 'sand']. I need to perform 4 corresponding POST queries with the form "/myapi/flower" and json data as POST. I then want to put the results into a list or whatever to use later at my leisure.

When I go synchronous, everything works well but of course this freezes up the UI.

$(document).ready(function() {
    //hang on event of form with id=myform
    $("#submitCall").click(function(e) {

        //prevent Default functionality
        e.preventDefault();
        $(this).text('Calculating...')

        setTimeout(function () {
            var mytypes = ['flower', 'grass', 'rock', 'sand'];

            responses=[];
            for(var i=0; i < mytypes.length; i++)
            {
                urlrequest = '/myapi/'+mytypes[i];
                jsonParams = JSON.stringify({"locationParams":{"name": ["myfirstparam", "mysecondparam"],
                                                               "value": [document.getElementsByName("amount_myfirstparam")[0].value, document.getElementsByName("amount_mysecondparam")[0].value]}})


                $.ajax({
                    url:urlrequest,
                    method:'post',
                    data:jsonParams,
                    async:false,
                    success: function(data){
                        console.log('success of ajax response')
                        responses.push(data['value'].meanValue);
                    },
                    error: function(xhr, textStatus, errorThrown ) {
                        console.log(urlrequest);
                    }
                });
            }

            delta = calculateDelta(responses);
            displayImpact(delta);


            $('#submitCall').text(' Update ');
        }, 20);

    });
});

In this case, the code does flower first, then push the result to responses, before moving on to grass, etc. All good, but as said, not ideal performance-wise.

Now, I'm trying to go asynchronous. And there, the queries get all completely mixed up whatever I do. I tried two main options that I thought should work:

Option1:

$(document).ready(function() {
    //hang on event of form with id=myform
    $("#submitCall").click(function(e) {

        //prevent Default functionality
        e.preventDefault();
        $(this).text('Calculating...')

        var mytypes = ['flower', 'grass', 'rock', 'sand'];

        var async_request=[];
        var responses=[];
        for(var i=0; i < mytypes.length; i++)
        {
            urlrequest = '/myapi/'+mytypes[i];
            jsonParams = JSON.stringify({"locationParams":{"name": ["myfirstparam", "mysecondparam"],
                                                           "value": [document.getElementsByName("amount_myfirstparam")[0].value, document.getElementsByName("amount_mysecondparam")[0].value]}})


            async_request.push($.ajax({
                url:urlrequest,
                method:'post',
                data:jsonParams,
                success: function(data){
                    console.log('success of ajax response')
                    responses.push(data['value'].meanValue);
                },
                error: function(xhr, textStatus, errorThrown ) {
                    console.log(urlrequest);
                }
            }));
        }

        $.when.apply(null, async_request).done( function(){
            // all done
            console.log('all request completed')
            console.log(responses);
        });

    });
});

Here, doing that gives me for example (somewhat inconsistent responses depending on the run):

flower --> grass response
grass --> flower response
rock --> flower response
sand --> sand response

I also tried:

Option2:

$(document).ready(function() {
    //hang on event of form with id=myform
    $("#submitCall").click(function(e) {

        var mytypes = ['flower', 'grass', 'rock', 'sand'];

        for (var i=0; i < mytypes.length; i++) {

            var $typ = mytypes[i]
            var $jsonParams = JSON.stringify({"locationParams":{"name": ["myfirstparam", "mysecondparam"],
                                                           "value": [document.getElementsByName("amount_myfirstparam")[0].value, document.getElementsByName("amount_mysecondparam")[0].value]}})

            var fn = function(index){
               $.ajax({
                  type: 'POST',
                  url: '/myapi/' + $typ,
                  data:$jsonParams,
                  success: function(data){
                              console.log(mytypes[index])
                              console.log(data)
                           }
                });
            };
            fn(i);
        }
    });
});

And now, I'm still getting mixed up results, though it seems a bit "less" mixed.

flower --> flower response
grass --> grass response
rock --> grass response
sand --> sand response

So... What's up? I have no idea how to debug that mess, any hints are welcome.

EDIT:

Option3:

$(document).ready(function() {
    //hang on event of form with id=myform
    $("#submitCall").click(function(e) {

        var jsonParams = JSON.stringify({"locationParams":{"name": ["myfirstparam", "mysecondparam"],
                                                           "value": [document.getElementsByName("amount_myfirstparam")[0].value, document.getElementsByName("amount_mysecondparam")[0].value]}})
        $.when(ajaxcall('flower', jsonParams), ajaxcall('grass', jsonParams), ajaxcall('rock', jsonParams), ajaxcall('sand', jsonParams)).done(function(aflower, agrass, arock, asand){


            console.log(aflower[0]);
            console.log(agrass[0]);
            console.log(arock[0]);
            console.log(asand[0]);
        });

    });
});

function ajaxcall(typ, jsonParams) {
    return $.ajax({
                type: 'POST',
                url:  '/myapi/' + typ,
                data: jsonParams
            });
}

Following the comments, I also tried this, but again, results get mixed up, some queries just don't return any results, some return another api call response...

EDIT:

Option 4:

$(document).ready(function() {
    //hang on event of form with id=myform
    $("#submitCall").click(function(e) {

        //prevent Default functionality
        e.preventDefault();
        $(this).text('Calculating...')
        var jsonParams = JSON.stringify({"locationParams":{"name": ["myfirstparam", "mysecondparam"],
                                                           "value": [document.getElementsByName("amount_firstparam")[0].value, document.getElementsByName("amount_secondparam")[0].value]}})
        var mytypes = ['flower', 'grass', 'rock', 'sand'];
        var urls = [
            '/myapi/flower',
            '/myapi/grass',
            '/myapi/rock',
            '/myapi/sand'
        ]
        var async_request=[];
        var responses=new Array(mytypes.length);
        for(var i=0; i < mytypes.length; i++)
        {
            urlrequest = urls[i];
            let position = i;
            async_request.push($.ajax({
                url:urlrequest,
                data:jsonParams,
                method:'POST',
                success: function(data){
                    console.log('success of ajax response')
                    console.log('position: ' + position);
                    console.log(data['typeString'])
                    console.log(mytypes[position])
                    responses[position] = data['myvalue'].meanValue;
                },
                error: function(xhr, textStatus, errorThrown ) {
                    console.log(urlrequest);
                }
            }));
        }

        $.when.apply(null, async_request).done( function(){
            // all done
            console.log('all request completed')
            console.log(responses);
        });

    });
});

Using this, the order still gets messed up, though I feel that I can see the code "trying its best", very rarely it works (2 times out of 30ish "clicks"). Most of the time one or two queries returns blank, and the wrong response is used (data at the flower position is actually the grass response).

I have to assume that something funky is going on at the API interface level, which I do not control. Note that the API call is made through a VPN to a remote machine, running a GeoServer REST API inside a Tomcat with a jar Java web app doing the heavy lifting. In a synchronous setting, everything works great so it may be like tomcat setting a lock on the jar resources used by the API or something like that (I'm way out of my depth here!)

William Abma
  • 415
  • 3
  • 14
  • 2
    Does this answer your question? https://stackoverflow.com/a/9865124/2181514 – freedomn-m Oct 20 '20 at 15:15
  • I'll try this, this answer does explain better what the elements in the done are and seems helpful. As an aside, would this be valid: $.when(ajax1('flower'), ajax1('grass'), ajax1('rock'), ajax1('sand')).done(function(a1, a2, a3, a4){ ... }); ? Or do the ajax functions need to be separated? – William Abma Oct 20 '20 at 15:26
  • Ok, so, it does not work. I'll update the main question with what I implemented and the results – William Abma Oct 20 '20 at 15:59

1 Answers1

0

To solve the problem you can first initialize the responses array with the same size as the mytypes array:

  var responses=new Array(mytypes.length);

Then in the for keep track of the iteration i storing it in another variable position:

for(var i=0; i < mytypes.length; i++){
  position = i;

  //code ommitted for brevity

}

And eventually in the callback function, insert the response to the right position in the responses array (instead of pushing it to the end of the array):

success: function(data){
  responses[position] = data['value'].meanValue;
}

Here is an adapted working snippet of this solution:

$(document).ready(function() {
    //hang on event of form with id=myform
  $("#submitCall").click(function(e) {

    //prevent Default functionality
    e.preventDefault();
    $(this).text('Calculating...')

    var mytypes = ['flower', 'grass', 'rock', 'sand'];
    var urls = [
        'https://run.mocky.io/v3/ff0bc991-7abc-4a43-91ad-04c29a74ae28',
        'https://run.mocky.io/v3/d32cc4fa-3f03-498a-9e07-53525e4eca7f',
        'https://run.mocky.io/v3/3ea1aa7e-dfbf-4f7a-a4ea-58c17d633e46',
        'https://run.mocky.io/v3/ea758cdf-60ce-49ea-a450-0c0bbdd5cd37'
    ]
    var async_request=[];
    var responses=new Array(mytypes.length);
    for(var i=0; i < mytypes.length; i++)
    {
        urlrequest = urls[i];
        let position = i;
        async_request.push($.ajax({
            url:urlrequest,
            method:'get',
            success: function(data){
                console.log('success of ajax response')
                console.log('position: ' + position);
                responses[position] = data['api'];
            },
            error: function(xhr, textStatus, errorThrown ) {
                console.log(urlrequest);
            }
        }));
    }

    $.when.apply(null, async_request).done( function(){
        // all done
        console.log('all request completed')
        console.log(responses);
    });

  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>


<input type="button" id="submitCall" value="submit ajax calls" />
francisco neto
  • 797
  • 1
  • 5
  • 13
  • Hmm. I tried exactly that but it's still failing. So I got to assume that the issue comes from the API and how it deals with concurrent calls or something probably... – William Abma Oct 20 '20 at 17:46
  • could you edit your post again and include that option 4 so I can see the code you tried...? – francisco neto Oct 20 '20 at 18:04
  • Sorry for the delay, I've edited the post to add in the option 4 – William Abma Oct 20 '20 at 18:35
  • Hmm, looking at it, I may be using a deprecated version of jquery? Maybe that would be one issue, I'll test with the cloudfare link. – William Abma Oct 20 '20 at 18:37
  • Ok, not sure if that's an effect of the updated jquery, but it seems that it's a lot more "solid" now, with basically zero (success) or one call getting mixed up with another one. So still not usable, but it feels frustratingly close. – William Abma Oct 20 '20 at 18:44