简体   繁体   中英

How to know if XMLHttpRequest has finished?

I'm uploading multiple files with form field

<input type="file" accept="image/jpeg, image/png" multiple 

a loop sends all files to upload function

       // Upload photo function
            var upload = function (photo, callback) {
                var formData = new FormData();
                formData.append('photo', photo);
                var request = new XMLHttpRequest();
                request.onreadystatechange = function() {
                    if (request.readyState === 4) {
                        callback(request.response);
                    }
                }
                request.open('POST', 'upload.php');
                request.responseType = 'json';
                request.send(formData);
            };  


    for (var i = 0; i < files.length; i++) {
        upload(resizedFile, function (response) { });
    }

if all files are successfully uploaded, want to redirect..

//all files finished, go to ...
if(response !== null && typeof response !== 'object') var response = JSON.parse(response);
window.location.href = "my-new-url.php?id="+response.id;

my problem, the redirect is done before all uploads are finished. how can I make the script wait for all xmlhttprequests to finish?


EDIT

I changed the upload function like this, to upload with jQuery. The fuction is called several times from the for loop, so how can I wait for all loops/jquery calls are finished, then redirect?

function upload (photo, callback) {
        var fd = new FormData();
        fd.append('photo',photo);

        return $.ajax({
            url: 'upload.php',
            type: 'post',
            data: fd,
            contentType: false,
            processData: false,
            success: function(response){

            },
        });
    };

You can increment a global counter variable in the callback. When it reaches the length of the array, all the requests have completed.

var completed = 0;
for (var i = 0; i < files.length; i++) {
    upload(resizedFile, function (response) {
        if (++completed == files.length) {
            window.location = redirect_url;
        }
    });
}

First of all, I would promisify the upload function. With JQuery would be much shorter as $.ajax returns a thenable object, but with old-fashioned vanilla JS, it would look like this:

var upload = function (photo) {
    return new Promise(function(resolve, reject) {
        var request = new XMLHttpRequest();

        request.onload = function() {
            if (request.status >= 200 && request.status < 300) {
                resolve(request.response);
            } else {
                reject(request.statusText);
            }
        };      
        request.onerror = function() { reject(request.statusText) }
        request.open('POST', 'upload.php');
        request.responseType = 'json';

        var formData = new FormData();
        formData.append('photo', photo);
        request.send(formData);
    })
}

See also: http://ccoenraets.github.io/es6-tutorial-data/promisify/

Then I would collect these promises in an array:

var uploads = files.map(function(file) { return upload(resizeFile(file)); })

(or simply iterate as you did and push them in an array)

Then you can use this code to handle the completion of all uploads:

Promise.all(uploads)
   .then(function(results) { /* do the redirect */ })
   .catch(function(error) { console.log(error); });

See also: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

Note: Why is counting suggested in an other answer wrong? Because it does not handle the condition when any of the requests fails. You will not be able to detect that case, and you will never reach the condition to be satisfied. Promises (and async/await) are the right way of handling asynchronous operations, like ajax requests.

[Update]

Here is the JQuery approach using the latest JS language features:

var upload = (photo) => {
   photo = resizeFile(photo);

   let formData = new FormData();
   formData.append('photo', photo);
   return $.ajax({
     type: 'POST',
     url: 'upload.php',
     data: formData
   }) // check other the options of ajax method, you might need them
}

let uploads = files.map(upload);

$.when.apply($,uploads)
  .done(_ => { /* do the redirect */ })
  .fail(e => { /* handle error */ })

The latter part can be written with async/await syntax:

async somefunction(){ 

  // rest of the code

  try {
     await $.when.apply($,uploads)
     /* do the redirect */
  } catch(ex) {
     /* handle error */
  }
}

Note, that $.ajax returns Deferred , which is a JQuery object, compatible with Promise.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM