web-dev-qa-db-fra.com

En JavaScript, comment utiliser/devrais-je utiliser async/wait avec XMLHttpRequest?

Divulgation complète: je me qualifierais comme ayant une connaissance intermédiaire de JavaScript. C'est donc légèrement au-dessus de mon niveau d'expérience pour le moment.

J'ai une extension Google Chrome qui fait une demande AJAX pour un file:/// local dès le chargement d'une page. Après avoir reçu la réponse de la demande, j'utilise le code renvoyé dans plusieurs fonctions plus tard dans mon code. La plupart du temps, je reçois la réponse avant que le code qui en a besoin ne soit exécuté. Mais parfois je ne le fais pas et tout se casse.

Maintenant, je suppose que je pourrais simplement insérer tout le code pertinent dans le xhr.onload ci-dessous. Mais cela semble vraiment inefficace? J'ai beaucoup de pièces en mouvement qui dépendent de la réponse et il semble mal de toutes les inclure.

J'ai parcouru plusieurs articles liés à async/wait et j'ai du mal à saisir le concept. Je ne suis pas non plus 100% positif, je regarde cela de la bonne façon. Devrais-je même envisager d'utiliser async/wait?

Voici le code de ma demande AJAX.

  var xhr = new XMLHttpRequest();
  xhr.open("GET", url, true);
  xhr.onload = function(e) {
    code = xhr.response;
  };
  xhr.onerror = function () {
    console.error("** An error occurred during the XMLHttpRequest");
  };
  xhr.send();

Disons que j'ai un tas de fonctions qui doivent être activées plus tard dans mon code. En ce moment, ils ressemblent juste à:

function doTheThing(code) {
  // I hope the response is ready.
}

Quelle est la meilleure façon d'aborder cela? Pour votre information, l'API Fetch n'est pas une option. 

Voici une vue d'ensemble de la structure de mon code.

// AJAX request begins.

// ...

// A whole bunch of synchronous code that isn't dependant on 
// the results of my AJAX request. (eg. Creating and appending
// some new DOM nodes, calculating some variables) I don't want
// to wait for the AJAX response when I could be building this stuff instead.

// ...

// Some synchronous code that is dependant on both my AJAX 
// request and the previous synchronous code being complete.

// ...

// Some more synchronous code that needs the above line to 
// be complete.
9
jkupczak

Je fais habituellement async/wait comme ceci:

async function doAjaxThings() {
    // await code here
    let result = await makeRequest("GET", url);
    // code below here will only execute when await makeRequest() finished loading
    console.log(result);
}
document.addEventListener("DOMContentLoaded", function () {
    doAjaxThings();
    // create and manipulate your DOM here. doAjaxThings() will run asynchronously and not block your DOM rendering
    document.createElement("...");
    document.getElementById("...").addEventListener(...);
});

Fonction xhr promis ici:

function makeRequest(method, url) {
    return new Promise(function (resolve, reject) {
        let xhr = new XMLHttpRequest();
        xhr.open(method, url);
        xhr.onload = function () {
            if (this.status >= 200 && this.status < 300) {
                resolve(xhr.response);
            } else {
                reject({
                    status: this.status,
                    statusText: xhr.statusText
                });
            }
        };
        xhr.onerror = function () {
            reject({
                status: this.status,
                statusText: xhr.statusText
            });
        };
        xhr.send();
    });
}
7
Thắng Trần Xuân

Vous avez deux options, 

la première consiste à utiliser la nouvelle api fetch qui est basée sur une promesse, avec ce que vous pouvez faire.

let response = await fetch(url);
response = await response.json();; // or text etc..
// do what you wanna do with response

Une autre option si vous voulez vraiment utiliser XMLHttpRequest est de le promettre

let response = await new Promise(resolve => {
   var xhr = new XMLHttpRequest();
   xhr.open("GET", url, true);
   xhr.onload = function(e) {
     resolve(xhr.response);
   };
   xhr.onerror = function () {
     resolve(undefined);
     console.error("** An error occurred during the XMLHttpRequest");
   };
   xhr.send();
}) 
// do what you wanna do with response

solution complète possible

(async () => {
   let response = await new Promise(resolve => {
      var xhr = new XMLHttpRequest();
      xhr.open("GET", url, true);
      xhr.onload = function(e) {
        resolve(xhr.response);
      };
      xhr.onerror = function () {
        resolve(undefined);
        console.error("** An error occurred during the XMLHttpRequest");
      };
      xhr.send();
   }) 
   doTheThing(response)
})()
4
Jiby Jose

Je crée une promesse pour le XHR. Ensuite, utilisez simplement await dans une fonction async pour l’appeler.

function getHTML(url) {
    return new Promise(function (resolve, reject) {
        var xhr = new XMLHttpRequest();
        xhr.open('get', url, true);
        xhr.responseType = 'document';
        xhr.onload = function () {
            var status = xhr.status;
            if (status == 200) {
                resolve(xhr.response.documentElement.innerHTML);
            } else {
                reject(status);
            }
        };
        xhr.send();
    });
}

async function schemaPageHandler(){
    try {
        var parser = new window.DOMParser();
        var remoteCode = await getHTML('https://schema.org/docs/full.html');
        var sourceDoc = parser.parseFromString(remoteCode, 'text/html');
        var thingList = sourceDoc.getElementById("C.Thing");
        document.getElementById("structured-data-types").appendChild(thingList);
    } catch(error) {
        console.log("Error fetching remote HTML: ", error);
    }              
}
2
Ron Royston

Vous pouvez par exemple créer une classe asynchrone à utiliser à la place de celle d'origine. Il manque certaines méthodes mais cela peut servir d'exemple.

(function() {
    "use strict";
    
    var xhr = Symbol();
    
    class XMLHttpRequestAsync {
        constructor() {
            this[xhr] = new XMLHttpRequest();
        }
        open(method, url, username, password) {
            this[xhr].open(method, url, true, username, password);
        }
        send(data) {
            var sxhr = this[xhr];
            return new Promise(function(resolve, reject) {
                var errorCallback;
                var loadCallback;
                
                function cleanup()  {
                    sxhr.removeEventListener("load", loadCallback);
                    sxhr.removeEventListener("error", errorCallback);
                }
                
                errorCallback = function(err) {
                    cleanup();
                    reject(err);
                };
                
                loadCallback = function() {
                    resolve(xhr.response);
                };
                
                
                sxhr.addEventListener("load", loadCallback);
                sxhr.addEventListener("error", errorCallback);
                
                
                sxhr.addEventListener("load", function load() {
                    sxhr.removeEventListener("load", load);
                    resolve(sxhr.response);
                });
                sxhr.send(data);
            });
        }
        set responseType(value)
        {
            this[xhr].responseType = value;
        }
        setRequestHeader(header, value) {
            this[xhr].setRequestHeader(header, value);
        }
    }
    
    addEventListener("load", async function main() {
        removeEventListener("load", main);


        var xhra = new XMLHttpRequestAsync();
        xhra.responseType = "json";
        xhra.open("GET", "appserver/main.php/" + window.location.hash.substring(1));
        console.log(await xhra.send(null));
        
    });
    
}());

0
user2704215