0

I am loading results in batches and looking for a solution that will prevent the screen from freezing until all my ajax calls have returned. Someone recommended using async promises (is that the correct solution?) but I don't understand how the syntax works to pass parameters between the chained calls.

It's the equivalent of this looped chain of many ajax calls except I need all calls to depend on the result from the previous call (in this example the loops for url1 all fire simultaneously which is not what I want). The run should end when the returned boolean "proceed" (from any of the ajax calls) is false, not when the last loop and url have been reached.

for (let i = 0; i < numLoops; i++) {
$.ajax({ 
          url: url1,
          type: "POST",
          data : jQuery.param({loop: i}),
          success: function(response) {
          var result = JSON.parse(response);

          if(result['proceed']){
                $.ajax({ 
                  url: url2,
                  success: function(response) {
                  var result = JSON.parse(response);
                  $( "#load" ).html(result['results']);
                  if(result['proceed']){ ... and so on

I am trying to use jquery .when .then promises with these functions:

function First(loop, proceed, updated){
if(proceed)
    {
    $.ajax({
                  url: url1,
                  type: "POST",
                  data : jQuery.param({loop: loop}),
                  success: function(response) {
                  var result = JSON.parse(response);
                  $( "#load" ).html(result['results']);
                  updated(result['proceed']);
                }
            });
    }
}
function Second(proceed, updated){
if(proceed)
    {
    $.ajax({
                  url: url2,
                  success: function(response) {
                  var result = JSON.parse(response);
                  $( "#load" ).html(result['results']);
                  updated(result['proceed']);
                }
            });
    }
} 
function Third(proceed, updated){
if(proceed)
    {
    $.ajax({
                  url: url3,
                  success: function(response) {
                  var result = JSON.parse(response);
                  $( "#load" ).html(result['results']);
                  updated(result['proceed']);
                }
            });
    }
} 

I'm having a hard time figuring out how to chain them so that the return from previous function is passed to the next function.

This incorrect syntax describes what I'm trying to do:

var proceed=true;
for (let i = 0; i < numLoops; i++) {
 $.when(First(i, proceed, updated); function updated(content) {var proceed=contents;} )
 .then(Second(proceed, updated); function updated(content) {var proceed=contents;})
 .then(Third(proceed, updated); function updated(content) {var proceed=contents;})
 }

How to pass updated proceed from First to Second? How to pass updated proceed from Third to First at end of each loop? I'm not super versed with javacript and would be most grateful for pointers. Thanks!

Stephanie
  • 108
  • 12
  • Yes, you should use promises and `async`/`await`. Did you try learning them? What do you not understand about them? – Bergi Jan 10 '22 at 19:39

1 Answers1

1

First, convert the $.ajax calls into real Promise objects, as described in this thread:

function asyncAjax(options){
    return new Promise(function(resolve, reject) {
        options.success = resolve;
        options.error = reject;
        $.ajax(options);
    });
}

Alternatively, use the Fetch API, which supports promises by default.

Then use an async function to make the requests:

for (let i = 0; i < numLoops; i++) {
    let response = await asyncAjax({ 
        url: url1, 
        type: "POST", 
        data: jQuery.param({loop: i}) 
    });
    
    let result = JSON.parse(response);
    if (result['proceed']) {
        response = await asyncAjax({ url: url2 });
        result = JSON.parse(response);
        ...
    }
}
Richard Deeming
  • 29,830
  • 10
  • 79
  • 151
  • Thank you! I am getting await is only valid in async functions, async generators and modules error so will have to study this a bit more. Does the loop 0 url3 proceed go back to loop 1 url1 through the first let response=await asyncAjax? – Stephanie Jan 10 '22 at 17:19
  • You'll need to add the `async` modifier to the function. If you're not in a function, and you're writing the loop in top-level code, you'll need to wrap it in an `async function` and call it from the top-level code instead. – Richard Deeming Jan 10 '22 at 17:24
  • The code will execute one request at a time. It behaves as if it were synchronous code, but without blocking the browser waiting for the AJAX request to complete. So it will execute `url1` loop 1, then `url2`, then `url3`, then `url1` loop 2, etc. – Richard Deeming Jan 10 '22 at 17:25
  • omg it is a thing of beauty. thank you so much! works like a charm. – Stephanie Jan 10 '22 at 17:39
  • "*first convert the $.ajax calls into real Promise objects*" - no need for that. You can directly `await` them. – Bergi Jan 10 '22 at 19:37
  • @Bergi Doesn't that depend on the jQuery version being used? IIRC, the `Deferred` object returned by older versions wasn't compatible with Javascript promises. – Richard Deeming Jan 11 '22 at 08:24
  • @RichardDeeming It did not conform to the Promises/A+ standard until jQuery 3.0 (and until jQuery 1.8, didn't have chaining at all), but it was always (since the introduction of deferreds in jQuery 1.5) a thenable that could be resolved to a proper promise. – Bergi Jan 11 '22 at 08:44