2

Estoy modificando unas funciones de JavaScript en una plantilla de Wordpress y he me he quedado bloqueado en el siguiente punto. Tengo una función que inspecciona los días que se ha reservado un alojamiento y los pinta según el tipo de reserva.

El código simplificado es el siguiente

function enableAllTheseDays(booking_id) {

    var reserva=booking_array[unixtime1];
    alert(reserva);//necesito aquí el valor

}

El código de la función que conecta con AJAX

function ajax_import_day_out(id_booking) {
    var ajaxurl     =   ajaxcalls_vars.admin_url + 'admin-ajax.php';
    var result;
    var aux =jQuery.ajax({
        type: 'POST',
        url: ajaxurl,
        data: {
            'action'                  :   'wpprueba_get_day_out',
            'booking_id'              :   id_booking,

        },
        success: function (data) {
            result=data;
    },
    error: function (errorThrown) {}
});//end ajax  
return result;  
}

He probado haciendolo sincrono pero no me vale la experiencia de navegación es muy mala y también he probado con lo siguiente:

function enableAllTheseDays(booking_id) {

   var auxiliar; //necesito que este valor se modifique en el interior de la función
      ajax_import_day_out(reserva, function(resultadoGlobal)
      {
            if (resultadoGlobal!='')
            {
                auxiliar=1;

            }
            else{
                auxiliar=0;
            }
        }); 
    alert(auxiliar);
}

El código de la función que conecta con AJAX

function ajax_import_day_out(id_booking,my_callback) {
    var ajaxurl     =   ajaxcalls_vars.admin_url + 'admin-ajax.php';
    var resultadoGlobal="";
    jQuery.ajax({
       type: 'POST',
       url: ajaxurl,
       data: {
        'action'                  :   'wpprueba_get_day_out',
        'booking_id'              :   id_booking,           
    },
    success: function (data) {

             resultadoGlobal=data;
             my_callback(resultadoGlobal);

    },
    error: function (errorThrown) {}
  });
  return resultadoGlobal;

  }

Puede ser que haya enfocado mal la resolución del código. Agradecería cualquier ayuda. Gracias.

Perdón esto que escribo aquí es una aclaración:

La cuestión es que después de la llamada a la función ajax tengo que trabajar con el valor auxiliar no solo mostrarlo con alert. Porque luego a su vez hay otra función recogerá el return de EnableAllTheseDays para trabajar también con el valor devuelto.

function enableAllTheseDays(booking_id) {

    var auxiliar; //necesito que este valor se modifique en el interior de la función
    ajax_import_day_out(reserva, function(resultadoGlobal)
    {
       if (resultadoGlobal!='')
       {
           auxiliar=1;

       }
       else{
           auxiliar=0;
       }
    }); 
    //trabajo con auxiliar
    return auxiliar;
}

function check_in_out_enable2(in_date, out_date) {

   var a = enableAllTheseDays(booking_id);
   // trabajo con a
}

Es la primera vez que trabajo con AJAX y voy un poco perdido con la cadena de ejecución. Si no es posible guardarlo y trabajar con ese valor se me ocurre que posiblemte debería simplificar el código y eliminar funciones intermedias. No sé si alguna otra libría de JavaScript me ayudaría a realizar mi proposito. Agradezco todas las aportaciones. MUCHAS GRACIAS.

Labrador99
  • 21
  • 1
  • 1
  • 4
  • 3
    Posible duplicado de [¿Como obtener la respuesta de una llamada ajax fuera de ella?](http://es.stackoverflow.com/questions/1539/como-obtener-la-respuesta-de-una-llamada-ajax-fuera-de-ella) – Wilfredo May 20 '16 at 16:25

2 Answers2

3

No deberías en ningún caso realizar peticiones Ajax síncronas.

De hecho si las haces, aunque te funcione, puedes ver en la consola de las herramientas de desarrollo de tu navegador que aparecerá un mensaje del tipo: Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/.

Las peticiones síncronas de XMLHttpRequest están "deprecadas" y tarde o temprano dejarán de estar soportadas en los navegadores. De hecho la recomendación actual del estándar es que generen una excepción en lugar de un aviso, aunque por ahora todos los navegadores (al menos los principales) siguen soportándolas.

La manera correcta de hacerlo es la que implementas en tu ejemplo:

function enableAllTheseDays(booking_id) {

   var auxiliar; //necesito que este valor se modifique en el interior de la función
   ajax_import_day_out(reserva, function(resultadoGlobal)
      {
            if (resultadoGlobal!='')
            {
                auxiliar=1;

            }
            else{
                auxiliar=0;
            }
        }); 
        alert(auxiliar);
}

El problema aquí es que, como la petición Ajax es asíncrona el código de la función callback no se ejecutará hasta que finalice la petición, mientras que el alert se ejecuta nada más realizarse ésta. Si metes el alert dentro de la función verás que el valor cambia correctamente:

function enableAllTheseDays(booking_id) {

   var auxiliar; //necesito que este valor se modifique en el interior de la función
   ajax_import_day_out(reserva, function(resultadoGlobal)
      {
            if (resultadoGlobal!='')
            {
                auxiliar=1;

            }
            else{
                auxiliar=0;
            }
            alert(auxiliar);
        }); 
}

Existen librerías que simulan el comportamiento síncrono de las peticiones (como puede ser radioactive) pero no te las recomiendo como solución.

A mí se me ha dado el caso de tener que adaptar una aplicación muy grande que hacía uso intensivo de este tipo de peticiones y he acabado descartando todas las opciones que he encontrado (de hecho hicimos una prueba con radioactive que era la que más prometía pero nos abría muchos otros problemas).

Lo que deberías hacer es refactorizar tu código para que las llamadas que necesiten este funcionamiento asíncrono se realicen con funciones de callback o promesas.

Por ejemplo, en tu caso:

function enableAllTheseDays(booking_id, callbackFunction) {

    var auxiliar; //necesito que este valor se modifique en el interior de la función
    ajax_import_day_out(reserva, function(resultadoGlobal)
    {
        if (resultadoGlobal!='')
        {
            auxiliar=1;

        }
        else{
            auxiliar=0;
        }
        callbackFunction(auxiliar);
   }); 
}

function check_in_out_enable2(in_date, out_date) {

     enableAllTheseDays(booking_id, function(a){
       // trabajo con a
     });
}

Al llamar a la función enalbleAllTheseDays le pasas la función a la que debe devolver el valor y, tras la petición ajax llamas a esta función con el valor calculado.

Asier Villanueva
  • 14,299
  • 2
  • 13
  • 31
2

La respuesta corta es no. Una petición ajax funciona de manera asíncrono y es por eso que si intentas devolver una variable y asignarla a otra devolverá undefined. Es por ello que tienes que acostumbrarte a escribir código de forma asíncrona. Una forma como han comentado son los callbacks, aunque este tipo de pattern puede llevar a hacer el código poco legible. Para tu ejemplo deberías hacer algo así :

function funcionAsync (datos, callback){
  jQuery.ajax({
    type: 'POST',
    url: ajaxurl,
    data: datos,
    success: function (data) {
       callback(null, data);
    },
    error: function (errorThrown) {callback(errorThown);}
  });
}

Y para la ejecución de la función :

funcionAsync(mis_datos, function(errorLanzado, datosDevueltos){
  if(errorLanzado) // Ha habido un error, deberías manejarlo :/
    return;
  // Tu código para hacer algo con datosDevueltos va aquí
});

Desde ECMAESCRIPT 6 existe una clase llamada Promise la cual es especialemnte util para trabajar con código asíncrono :

Ten en mente la función asíncrono de antes, con Promise harías algo así :

function funcionAsync (datos){
  // La primera diferencia es que no se le pasa un callback,
  // La función devuelve una Promise
  return new Promise(function(resolver, rechazar){
    jQuery.ajax({
      ...
      success : function(data){resolver(data)},
      error : function(error){rechazar(error)}
    });
  });
}

Ahora para utilizarla :

funcionAsync(datos)
.then(function(datosDevueltos){
  // Aquí el código para hacer algo con datosDevueltos
}, function(errorLanzado){
  // Aquí el código para hacer algo cuando ocurra un error.
});
Jose Hermosilla Rodrigo
  • 3,805
  • 1
  • 15
  • 24