Aymen
Aymen

Reputation: 839

Wait until unknown number of ajax request done

I'm uploading a list of documents to the server, and for each document I launch an ajax request, but the number of requests is unknown (Depends on the number of document being uploaded). How can I show a message to the user when all the documents are uploaded (All ajax requests are done).

$.each(files,function(idx,elm){
                    let formData = new FormData();
                    let ID = '_' + Math.random().toString(36).substr(2, 9);
                    formData.append('document_id', ID);
                    formData.append('file-doc', elm);
                    $.ajax({
                    url: "http://127.0.0.1:5000/add_multiple_docs",
                        type: 'POST',
                        data: formData,
                        cache: false,
                        contentType: false,
                        processData: false,
                        beforeSend: function (xhr, settings) {
                            function getCookie(name) {
                                var cookieValue = null;
                                if (document.cookie && document.cookie !== '') {
                                    var cookies = document.cookie.split(';');
                                    for (var i = 0; i < cookies.length; i++) {
                                        var cookie = jQuery.trim(cookies[i]);
                                        // Does this cookie string begin with the name we want?
                                        if (cookie.substring(0, name.length + 1) === (name + '=')) {
                                            cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                                            break;
                                        }
                                    }
                                }
                                return cookieValue;
                            }

                            if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
                                // Only send the token to relative URLs i.e. locally.
                                xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
                            }
                        },
                        success: function (data) {
                            alert(data);
                        },
                        failure: function (request) {
                            console.log(request);
                        },
                        error: function (jqXHR, exception) {
                            console.log("error add document");
                            let msg = '';
                            if (jqXHR.status === 0) {
                                msg = 'Not connect.\n Verify Network.';
                            } else if (jqXHR.status === 404) {
                                msg = 'Requested page not found. [404]';
                            } else if (jqXHR.status === 500) {
                                msg = 'Internal Server Error [500].';
                            } else if (exception === 'parsererror') {
                                msg = 'Requested JSON parse failed.';
                            } else if (exception === 'timeout') {
                                msg = 'Time out error.';
                            } else if (exception === 'abort') {
                                msg = 'Ajax request aborted.';
                            } else {
                                msg = 'Uncaught Error.\n' + jqXHR.responseText;
                            }
                            console.log(msg)
                        }
                    });
                });                
            }

Upvotes: 0

Views: 509

Answers (3)

tstephen
tstephen

Reputation: 1

A very interesting question.

I had a similar issue when trying to get data from the youtube API which only returned a max result set of 50 items.

To solve this problem I used a recursive function that accepted a callback, on base case (in my case when there was no nextPageToken) I called the callback function.

The recursion was triggered in the success handler of the $.ajax request.

    function fetchVideos(nextPageToken, callback) {
        if (nextPageToken === null || nextPageToken === undefined) {
            callback(null);
            return;
        }

        $.ajax(requestURI + `&pageToken=${nextPageToken}`, {
            success: (data) => {
                // use data somehow?
                fetchVideos(data.nextPageToken, callback);
            },

            error: (err) => {                    
                callback(err);
            }
        })
    }

    fetchVideos("", (err) => {
        if (err) {
            console.log(err.responseJSON);
            return;
        }
        updateUI();
    })

When there was no longer a nextPageToken in the response it would trigger the if statement. I think it works kind of sync? Because I need the nextPageToken to perform the next request.

I know this is not the best case for you situation, but I answered based on the title which is how I came to this page :)

Upvotes: 0

Mugentoki
Mugentoki

Reputation: 1616

First of all, you know how many files are uploaded, so you know how many ajax request you are doing. (1 Request per file)

So before your $.each() fires you get the size of files

let count = $(files).size();

$.each(files,function(idx,elm){
/*your code with requests*/
}

Now, after each ajax request hast fired, decrement count. Decrement inside your success and failure methods, because it doesn't mattet if it succeeded or not. And check if count === 0. If it's 0 than you know all ajax are done.

$.ajax({
/*your other settings*/
    success: function (data) {
        alert(data);
        count--;

        doSomething(count);
    },
    failure: function (request) {
        console.log(request);
        count--;

        doSomething(count);
    },
});

function doSomething(count){
    if(count === 0){
        /*stuff you wannna do after all ajax requests are done*/
    }
}

I haven't done that many ajax for now, so I'm not quite sure if failure is also fired on error, but if not maybe add count-- and the if on error as well.

Upvotes: 2

Rory McCrossan
Rory McCrossan

Reputation: 337560

To achieve what you need you can place all the jqXHR objects returned from $.ajax() in an array which you can apply() to $.when(). Then you can execute whatever logic you require after all of those promises have been resolved. Try this:

var promises = files.map(function(elm) {
  // setup formData...

  return $.ajax({
    url: "http://127.0.0.1:5000/add_multiple_docs",
    // ajax settings...
  });
});

$.when.apply($, promises).done(function() {
  console.log('all requests complete, do something here...');
});

However, it's definitely worth noting that sending AJAX requests in a loop is not a scalable pattern to use. It would be a much better idea to aggregate all the file and related data in a single AJAX request and handle that once on the server.

Upvotes: 1

Related Questions