AJAX Javascript Pure Asynchronous

6

I am trying to retrieve a data coming from Ajax to use in another function, but I am not succeeding. In jQuery I do this using a parameter called async: false .

But in pure JS I can not do it. The parameter is false in the .open function.

JS

function initMap() {
    var idDealer = document.getElementById('id-concessionaria').value;
    urlBase      = document.getElementsByTagName('body')[0].getAttribute('data-base');

    // Ajax Procura na Base de Dados Latitude e Longitude da Concessionária

    var request  = new XMLHttpRequest();
    request.open('POST', urlBase + '/avaliar-concessionaria/lat-lng', false);
    request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    request.send("id="+idDealer);

    request.onload = function() {
        if (request.status >= 200 && request.status < 400) {
            var data = JSON.parse(request.responseText);
            var lat  = data[0];
            var lng  = data[1];
        }
    };

    // Quero usar as variáveis aqui

    var myLatLng = { lat: lat, lng: lng }
    map = new google.maps.Map(document.getElementById('mapa'), {
        center: myLatLng,
        zoom: 10
    });

    var marker = new google.maps.Marker({
        position: myLatLng,
        map: map,
        title: 'Aqui!'
    });
}
    
asked by anonymous 10.03.2016 / 12:16

2 answers

4

You should use the event readystatechange and not load .

In any case, I find it unnecessary to use the header application/x-www-form-urlencoded , the default multipart/form-data is even better.

Possible values for readyState are:

  • 0 - Not Sent
  • 1 - Open method was executed.
  • 2 - Send method has been called and Headers are now available.
  • 3 - Loading, the [responseText] property already has partial data.
  • 4 - Operation Completed.

Then we should wait for readyState == 4 so we can work on the return of the request.

var idDealer = document.getElementById('id-concessionaria').value;
urlBase      = document.body.dataset.base;

var request  = new XMLHttpRequest();
//terceiro parametro [async] é por default true.
request.open('POST', urlBase + '/avaliar-concessionaria/lat-lng'); 

//não acho necessario, prefiro o "multipart/form-data";
//request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); 

request.responseType = "json"; //formato esperado para o retorno.
request.addEventListener("readystatechange", function() {
    if (request.readyState == 4) {
        //normalmente uso apenas o status 200 para verificar se é sucesso.
        if (request.status >= 200 && request.status < 400) { 
            //devido ao request.responseType = "json" não é preciso deserializar o responseText.
            //var data = JSON.parse(request.responseText); 
            var lat  = request.response[0];
            var lng  = request.response[1];

            var myLatLng = { lat: lat, lng: lng }
            map = new google.maps.Map(document.getElementById('mapa'), {
                center: myLatLng,
                zoom: 10
            });

            var marker = new google.maps.Marker({
                position: myLatLng,
                map: map,
                title: 'Aqui!'
            });
        }       
    }   
});

//montando os dados a serem enviados.
//opcionalmente o FormData pode receber como parametro um form, 
//desta forma ele já será preenchido com os dados do form automaticamente.
//caso contrario, use o append para incluir os valores a serem enviados.
var formData = new FormData();
formData.append("id", idDealer);
request.send(formData);

To do this synchronously, you do not need any events, just run the code in a linear way, but doing so will block the thread, so it is not advisable.

var idDealer = document.getElementById('id-concessionaria').value;
urlBase      = document.body.dataset.base;

var request  = new XMLHttpRequest();
request.open('POST', urlBase + '/avaliar-concessionaria/lat-lng', false); 

//não acho necessario, prefiro o "multipart/form-data";
//request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); 

request.responseType = "json"; //formato esperado para o retorno.

//montando os dados a serem enviados.
//opcionalmente o FormData pode receber como parametro um form, 
//desta forma ele já será preenchido com os dados do form automaticamente.
//caso contrario, use o append para incluir os valores a serem enviados.
var formData = new FormData();
formData.append("id", idDealer);
request.send(formData);

//normalmente uso apenas o status 200 para verificar se é sucesso.
if (request.status >= 200 && request.status < 400) { 
    //devido ao request.responseType = "json" não é preciso deserializar o responseText.
    //var data = JSON.parse(request.responseText); 
    var lat  = request.response[0];
    var lng  = request.response[1];

    var myLatLng = { lat: lat, lng: lng }
    map = new google.maps.Map(document.getElementById('mapa'), {
        center: myLatLng,
        zoom: 10
    });

    var marker = new google.maps.Marker({
        position: myLatLng,
        map: map,
        title: 'Aqui!'
    });
}
    
10.03.2016 / 12:30
3

I know you already got the answer, but I would like to explain how to use XmlHttpRequest API .

When you set false to the third parameter of .open you are setting it to synchronous:

open('POST', url, false);

Even if you use .onload or .onreadystatechange they will not work, understand that Ajax is not the technology but the way we use XmlHttpRequest API , ie it will only be ajax if it is asynchronous, otherwise it is SJAX ( XmlHttpRequest sync).

See the differences:

Synchronous (this is not Ajax):

var oReq = new XMLHttpRequest();

//Defina como false
oReq.open("GET", "/url", false);

//Espera completar a requisição, geralmente congela o browser
oReq.send(null);

alert(oReq.responseText);
  

Note that the synchronous "freezes" the webbrowser while the request does not finish

Asynchronous (ie Ajax):

var oReq = new XMLHttpRequest();

//Defina como true
oReq.open("GET", "/url", true);

//Função assíncrona que aguarda a resposta
oReq.onreadystatechange = function()
{
    if (oReq.readyState === 4) {
        alert(oReq.responseText);
    }
};

//Envia a requisição, mas a resposta fica sendo aguardada em Background
oReq.send(null);
  

Note that the asynchronous does not freeze the browser, because the process is actually in Background and the signal is sent via callback pro .onreadystatechange to each stage of .readyState

As I explained in this question: Ajax is not a programming language. So what is it?

application/x-www-form-urlencoded vs multipart/form-data

I understand what TobyMosque said and I do not disagree, just that we need to understand the differences, see setRequestHeader('Content-Type', ...); is not something just XmlHttpRequest as you should know, it's an instruction that tells how the data should be interpreted by the back end.

Using new FormData works perfectly in the way explained, but if we do not have browser support for this class then we have to use application/x-www-form-urlencoded .

The setRequestHeader('Content-Type', ...); is equivalent to the enctype attribute in <form> , as we define how the form data will be encoded when sending them to the server, there are 3 types of values for this attribute:

  • application/x-www-form-urlencoded This is the default value in <form> , but not XmlHttpRequest . In it all characters are encoded before being sent, for example spaces are exchanged for + and special characters are converted to HEX ASCII values.

  • % of spaces are converted to multipart/form-data signs, but other characters will not be encoded.

Assuming the browser is a bit older and does not support text/plain then you have to use + and encode the past in FormData , like this:

var oReq = new XMLHttpRequest();

//Defina como true
oReq.open("POST", "/url", true);
oReq.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

//Função assíncrona que aguarda a resposta
oReq.onreadystatechange = function()
{
    if (oReq.readyState === 4) {
        alert(oReq.responseText);
    }
};

oReq.send('nome=' + escape('João de Nobrega'));

Or:

oReq.send('nome=' + encodeURIComponent('João de Nobrega'));

If you send without application/x-www-form-urlencoded they will be in the .send(...); format and if you do not use application/x-www-form-urlencoded or RAW it is likely that it will not recognize what comes after encodeURIComponent

Some details here: Upload does not work $ _FILES undefined index error

Conclusion

Do not use synchronous because it is deprecated and browsers are issuing warnings and in the future will remove the synchronous, so do not use escape , use João or omit the third parameter (by default false uses true ) like this:

request.open('POST', urlBase + '/avaliar-concessionaria/lat-lng');

How the code should stay

As I said, synchronization will soon no longer work in modern browsers, for this you need to understand the difference between callback and return, read this:

Your code should look like this:

function initMap() {
    var idDealer = document.getElementById('id-concessionaria').value;
    urlBase      = document.getElementsByTagName('body')[0].getAttribute('data-base');

    var myLatLng, marker, myLatLng; //Torna as variáveis acessíveis no escopo de 'initMap'

    var exec = function(lat, lng) {
        myLatLng = { "lat": lat, "lng": lng }
        map = new google.maps.Map(document.getElementById('mapa'), {
            center: myLatLng,
            zoom: 10
        });

        var marker = new google.maps.Marker({
            position: myLatLng,
            map: map,
            title: 'Aqui!'
        });

        //Resto da função
    };

    var request  = new XMLHttpRequest();
    request.open('POST', urlBase + '/avaliar-concessionaria/lat-lng', true);
    request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

    request.onreadystatechange = function() {
        if (oReq.readyState === 4 && request.status >= 200 && request.status < 400) {
            var data = JSON.parse(request.responseText);
            var lat  = data[0];
            var lng  = data[1];
            exec(lat, lng);
        }
    };
    request.send("id=" + idDealer);
}

This way it will work asynchronously.

    
10.03.2016 / 18:04