Fictizia / Curso-JS-para-desarrolladores-web_ed4

FICTIZIA » Curso de JavaScript para desarrolladores web — 4ª Edición
9 stars 11 forks source link

Callback con petición ajax #10

Closed davidfisher24 closed 8 years ago

davidfisher24 commented 8 years ago

He subido la mapa de las estaciones de bicing (he usado lo de Barcelona). Pregunta por que tengo que admitir que no tengo dominado el uso de callbacks. He sacado los datos de bicing con una peticion ajax, he empujado los datos en un array, y he usado el array para posicionar los markers con longitud y latitud. La unica manera que lo he conseguido era usar un window.settimeout, por que la funcion para anadir los markers ejecuta antes del termino de la petición. Imagino que tengo que usar un callback en la peticion ajax pero ni idea hacerlo.

UlisesGascon commented 8 years ago

Hola @davidfisher24 !

window.setTimeout() no es una forma correcta para resolver un problema de asincronía. código original:

var map;
var stations = [];

function initialize() {
    var latlng = new google.maps.LatLng(41.3873,2.1762);
    var myOptions = {
        zoom: 13,
        center: latlng,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    map = new google.maps.Map(document.getElementById("googleMap"), myOptions);
}

 function peticionAjax() {

    var xmlHttp = new XMLHttpRequest();
    url = "https://crossorigin.me/http://barcelonaapi.marcpous.com/bicing/stations.json";
    xmlHttp.onreadystatechange = function() {

        if(xmlHttp.readyState === 4){
            if (xmlHttp.status === 200) {
                var result = JSON.parse(xmlHttp.responseText);
                console.info(result);
                for (var i = 0; i<result.data.bici.length; i++) {
                    stations.push([result.data.bici[i].lat, result.data.bici[i].lon]);
                    }
             } else if (xmlHttp.status === 404) {
                            console.info(JSON.parse(xmlHttp.responseText));
                    }   
             }
        };
    xmlHttp.open("GET", url, true);
    xmlHttp.send();
}

function addStation() {
    for (var i = 0; i <= stations.length; i++) {
        var marker = new google.maps.Marker({
            position:  new google.maps.LatLng(stations[i][0], stations[i][1]),
        });
        marker.setMap(map);
    }
}

google.maps.event.addDomListener(window, 'load', initialize);
peticionAjax();
window.setTimeout(addStation, 1000);

En el caso de peticiones ajax, fácilmente podemos incluir funciones o cualquier cosa que necesitemos justo después que la petición ha sido resuelta o rechazada, ya que solamente se ejecutarán cuando la petición haya terminado. Código modificado:

var map;
var stations = [];

function initialize() {
    var latlng = new google.maps.LatLng(41.3873,2.1762);
    var myOptions = {
        zoom: 13,
        center: latlng,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    map = new google.maps.Map(document.getElementById("googleMap"), myOptions);
}

 function peticionAjax() {

    var xmlHttp = new XMLHttpRequest();
    url = "https://crossorigin.me/http://barcelonaapi.marcpous.com/bicing/stations.json";
    xmlHttp.onreadystatechange = function() {

        if(xmlHttp.readyState === 4){
            if (xmlHttp.status === 200) {
                var result = JSON.parse(xmlHttp.responseText);
                console.info(result);
                for (var i = 0; i<result.data.bici.length; i++) {
                    stations.push([result.data.bici[i].lat, result.data.bici[i].lon]);
                    }
                addStation()
             } else if (xmlHttp.status === 404) {
                            console.info(JSON.parse(xmlHttp.responseText));
                    }   
             }
        };
    xmlHttp.open("GET", url, true);
    xmlHttp.send();
}

function addStation() {
    for (var i = 0; i <= stations.length; i++) {
        var marker = new google.maps.Marker({
            position:  new google.maps.LatLng(stations[i][0], stations[i][1])
        });
        marker.setMap(map);
    }
}

google.maps.event.addDomListener(window, 'load', initialize);
peticionAjax();

Podemos aprovechar que tenemos la respuesta para simplificar.

  1. Eliminar la variable stations
  2. Eliminar la función addStation Otra versión del código:
var map;

function initialize() {
    var latlng = new google.maps.LatLng(41.3873,2.1762);
    var myOptions = {
        zoom: 13,
        center: latlng,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    map = new google.maps.Map(document.getElementById("googleMap"), myOptions);
}

 function peticionAjax() {

    var xmlHttp = new XMLHttpRequest();
    url = "https://crossorigin.me/http://barcelonaapi.marcpous.com/bicing/stations.json";
    xmlHttp.onreadystatechange = function() {

        if(xmlHttp.readyState === 4){
            if (xmlHttp.status === 200) {
                var result = JSON.parse(xmlHttp.responseText);

                for (var i = 0; i<result.data.bici.length; i++) {
                    var marker = new google.maps.Marker({
                        position:  new google.maps.LatLng(result.data.bici[i].lat, result.data.bici[i].lon),
                    });
                    marker.setMap(map);              
                }
             } else if (xmlHttp.status === 404) {
                            console.info(JSON.parse(xmlHttp.responseText));
                    }   
             }
        };
    xmlHttp.open("GET", url, true);
    xmlHttp.send();
}

google.maps.event.addDomListener(window, 'load', initialize);
peticionAjax();

Si necesitas crear una función con callback, puedes usar este ejemplo de la clase 3. Siempre que trabajemos con callbacks necesitamos comprobar que el parámetro que nos pasen sea una función, además en muchos casos nos interesa que el callback sea opcional.

var quieroCallback = function(parametro, callback){
    if ((callback) && (typeof callback === 'function')){
        callback(parametro);
    }
    else
        console.log(parametro, callback);
}

quieroCallback('a', 'b');

quieroCallback('a', function(val){
    console.log(val);
});
davidfisher24 commented 8 years ago

Muchas gracias. Mas sencillo que pensaba al final.

davidfisher24 commented 8 years ago

Otro pregunta y espero que no te moleste demasiado. Sobre Firebase. Quiero actualizar en realtime una lista como la del las peliculas que hemos visto, pero siempre ordenando la lista por un score. Es una lista de scores en un juego. El codigo es este.

function databaseUpdate(name, score) { database.push().set({ name: name, score: score }); }

database.orderByChild("score").on("child_added", function(snapshot){ databaseField.innerHTML += snapshot.val().name + " " + snapshot.val().score + "
"; });

Cuando carga la pagina todo bien, imprime todos los names y scores en orden desde firebase. Pero al añadir uno nuevo, solo añade al final de lista actual, no en orden, y solo ordena la lista cuando la pagina entera acualiza. Si pongo el codio de orderByChild dentro del funcion databaseUpdate, imprime el nuevo dato varios veces, y luego la lista entera ordenada. No estoy seguro de lo que esta pasando aqui (necesito un "forEach?") o si es practicable lo que quiero hacer.

UlisesGascon commented 8 years ago

Hola @davidfisher24 !

Me gusta verte tan activo :-)

No estoy seguro de que haya comprendido al 100% el problema con el ejemplo que me has pasado, pero te incluyo este código que espero sea de ayuda.

var database = new Firebase("https://URL.firebaseio.com/PATH");
var databaseField = document.body;

function databaseUpdate(name, score) {
    database.push().set({
        name: name,
        score: score
    })
    // Iniciamos la actualización de la vista
    updateUI();
};

function updateUI (){
    // Borramos los datos en el HTML
    databaseField.innerHTML = "";
    // Cargamos los datos actualizados
    database.orderByChild("score").on("child_added", function(snapshot){
          databaseField.innerHTML += snapshot.val().name + " " + snapshot.val().score + "<br>";
    });
}

// Se ejecuta al iniciarse el script
updateUI();

¿Cómo funciona?

davidfisher24 commented 8 years ago

Ahora si funciona gracias. No sé exactamente que tenía mal antes. Había intentado incluir la linea databaseField.innerHTML = ""; pero con este borraba todo, y solo imprimia el score mas actualizada, no todos. Subo el codigo entero luego. Gracias