¿Cómo puedo hacer que jQuery realice una solicitud Ajax sincrónica, en lugar de asincrónica?

StackOverflow https://stackoverflow.com/questions/133310

Pregunta

Tengo un widget de JavaScript que proporciona puntos de extensión estándar. Una de ellas es la función beforecreate . Debería devolver false para evitar que se cree un elemento.

He agregado una llamada Ajax a esta función usando jQuery:

beforecreate: function (node, targetNode, type, to) {
  jQuery.get('http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value),

  function (result) {
    if (result.isOk == false) 
        alert(result.message);
  });
}

Pero quiero evitar que mi widget cree el elemento, por lo que debería devolver false en la función madre, no en la devolución de llamada. ¿Hay alguna manera de realizar una solicitud AJAX sincrónica usando jQuery o cualquier otra API en el navegador?

¿Fue útil?

Solución

En la documentación de jQuery : especifica asíncrono La opción es false para obtener una solicitud Ajax síncrona. Luego, su devolución de llamada puede establecer algunos datos antes de que su función madre continúe.

A continuación se muestra el aspecto que tendría su código si se cambia como se sugiere:

beforecreate: function (node, targetNode, type, to) {
    jQuery.ajax({
        url: 'http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value),
        success: function (result) {
            if (result.isOk == false) alert(result.message);
        },
        async: false
    });
}

Otros consejos

Puede configurar el Ajax de jQuery en modo síncrono llamando a

jQuery.ajaxSetup({async:false});

Y luego realice sus llamadas Ajax usando jQuery.get (...);

Luego, simplemente vuelve a encenderlo una vez

jQuery.ajaxSetup({async:true});

Supongo que funciona de la misma manera sugerida por @Adam, pero podría ser útil para alguien que quiera reconfigurar su jQuery.get () o jQuery.post ( ) a la sintaxis jQuery.ajax () más elaborada.

¡Excelente solución! Cuando intenté implementarlo, noté que si devolvía un valor en la cláusula de éxito, volvía a estar indefinido. Tuve que almacenarlo en una variable y devolver esa variable. Este es el método que se me ocurrió:

function getWhatever() {
  // strUrl is whatever URL you need to call
  var strUrl = "", strReturn = "";

  jQuery.ajax({
    url: strUrl,
    success: function(html) {
      strReturn = html;
    },
    async:false
  });

  return strReturn;
}

Todas estas respuestas pierden el punto de que hacer una llamada Ajax con async: false hará que el navegador se bloquee hasta que se complete la solicitud de Ajax. El uso de una biblioteca de control de flujo resolverá este problema sin colgar el navegador. Aquí hay un ejemplo con Frame.js :

beforecreate: function(node,targetNode,type,to) {

    Frame(function(next)){

        jQuery.get('http://example.com/catalog/create/', next);
    });

    Frame(function(next, response)){

        alert(response);
        next();
    });

    Frame.init();
}
function getURL(url){
    return $.ajax({
        type: "GET",
        url: url,
        cache: false,
        async: false
    }).responseText;
}


//example use
var msg=getURL("message.php");
alert(msg);

Tenga en cuenta que si realiza una llamada Ajax entre dominios (utilizando JSONP ), usted < strong> no puede hacerlo de forma sincrónica, la bandera async será ignorada por jQuery.

$.ajax({
    url: "testserver.php",
    dataType: 'jsonp', // jsonp
    async: false //IGNORED!!
});

Para las llamadas JSONP que podrías usar:

  1. llame a Ajax a su propio dominio y realice la llamada entre dominios del lado del servidor
  2. Cambie su código para que funcione de forma asíncrona
  3. Utilice un " secuenciador de funciones " biblioteca como Frame.js (esta respuesta )
  4. Bloquee la IU en lugar de bloquear la ejecución (esta respuesta ) (mi forma favorita)

Nota: No debe usar async: false debido a estos mensajes de advertencia:

  

A partir de Gecko 30.0 (Firefox 30.0 / Thunderbird 30.0 / SeaMonkey 2.27), las solicitudes síncronas en el hilo principal han quedado en desuso debido a los efectos negativos para la experiencia del usuario.

Chrome incluso advierte sobre esto en la consola:

  

El XMLHttpRequest síncrono en el hilo principal está en desuso debido a sus efectos perjudiciales para la experiencia del usuario final. Para obtener más ayuda, consulte https://xhr.spec.whatwg.org/ .

Esto podría romper su página si está haciendo algo así, ya que podría dejar de funcionar cualquier día.

Si desea hacerlo de una manera que todavía se siente como si fuera síncrono pero todavía no se bloquea, entonces debería usar async / wait y probablemente también algo de ajax que se base en promesas como el nuevo Recuperar API

async function foo() {
  var res = await fetch(url)
  console.log(res.ok)
  var json = await res.json()
  console.log(json)
}

Editar chrome está trabajando en Deshabilitar la sincronización de XHR en la salida de la página cuando la página se está alejando o navegando por el usuario. Esto implica antes de descargar, descargar, ocultar páginas y cambiar la visibilidad.

Si este es tu caso de uso, entonces deberías echar un vistazo a navigator.sendBeacon en su lugar

También es posible que la página deshabilite los requisitos de sincronización con los encabezados http o el atributo de permiso del iframe

Con async: false usted obtiene un navegador bloqueado. Para una solución síncrona sin bloqueo, puede utilizar lo siguiente:

ES6 / ECMAScript2015

Con ES6 puede usar un generador & amp; la co library :

beforecreate: function (node, targetNode, type, to) {
    co(function*(){  
        let result = yield jQuery.get('http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value));
        //Just use the result here
    });
}

ES7

Con ES7 puedes usar asyc await:

beforecreate: function (node, targetNode, type, to) {
    (async function(){
        let result = await jQuery.get('http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value));
        //Just use the result here
    })(); 
}

Utilicé la respuesta dada por Carcione y la modifiqué para usar JSON.

 function getUrlJsonSync(url){

    var jqxhr = $.ajax({
        type: "GET",
        url: url,
        dataType: 'json',
        cache: false,
        async: false
    });

    // 'async' has to be 'false' for this to work
    var response = {valid: jqxhr.statusText,  data: jqxhr.responseJSON};

    return response;
}    

function testGetUrlJsonSync()
{
    var reply = getUrlJsonSync("myurl");

    if (reply.valid == 'OK')
    {
        console.dir(reply.data);
    }
    else
    {
        alert('not valid');
    }    
}

Agregué el tipo de datos de 'JSON' y cambié el .responseText a responseJSON .

También recuperé el estado usando la propiedad statusText del objeto devuelto. Tenga en cuenta que este es el estado de la respuesta de Ajax, no si el JSON es válido.

El back-end tiene que devolver la respuesta en JSON correcto (bien formado), de lo contrario el objeto devuelto será indefinido.

Hay dos aspectos a considerar al responder la pregunta original. Uno le dice a Ajax que realice sincrónicamente (configurando async: false ) y el otro está devolviendo la respuesta a través de la declaración de retorno de la función de llamada, en lugar de a una función de devolución de llamada.

También lo probé con POST y funcionó.

Cambié GET a POST y agregué datos: postdata

function postUrlJsonSync(url, postdata){

    var jqxhr = $.ajax({
        type: "POST",
        url: url,
        data: postdata,
        dataType: 'json',
        cache: false,
        async: false
    });

    // 'async' has to be 'false' for this to work
    var response = {valid: jqxhr.statusText,  data: jqxhr.responseJSON};

    return response;
}

Tenga en cuenta que el código anterior solo funciona en el caso de que asíncrono sea falso . Si tuviera que establecer async: true el objeto devuelto jqxhr no sería válido en el momento en que regrese la llamada AJAX, solo más tarde cuando la llamada asincrónica haya finalizado, pero eso es demasiado tarde para establecer la variable respuesta .

Este es un ejemplo:

$.ajax({
  url: "test.html",
  async: false
}).done(function(data) {
   // Todo something..
}).fail(function(xhr)  {
   // Todo something..
});

En primer lugar, debemos entender cuándo usamos $ .ajax y cuándo usamos $ .get / $. post

Cuando requerimos un control de bajo nivel sobre la solicitud ajax, como la configuración del encabezado de la solicitud, la configuración de almacenamiento en caché, la configuración síncrona, etc., entonces debemos optar por $ .ajax.

$ .get / $. post: cuando no requerimos un control de bajo nivel sobre la solicitud de ajax. Simplemente obtenga / publique los datos en el servidor. Es una abreviatura de

$.ajax({
  url: url,
  data: data,
  success: success,
  dataType: dataType
});

y, por lo tanto, no podemos usar otras funciones (sincronización, caché, etc.) con $ .get / $. post.

Por lo tanto, para un control de bajo nivel (sincronización, caché, etc.) sobre la solicitud ajax, deberíamos ir por $ .ajax

 $.ajax({
     type: 'GET',
      url: url,
      data: data,
      success: success,
      dataType: dataType,
      async:false
    });

esta es mi implementación simple para solicitudes ASYNC con jQuery. Espero que esto ayude a cualquiera.

var queueUrlsForRemove = [
    'http://dev-myurl.com/image/1', 
    'http://dev-myurl.com/image/2',
    'http://dev-myurl.com/image/3',
];

var queueImagesDelete = function(){

    deleteImage( queueUrlsForRemove.splice(0,1), function(){
        if (queueUrlsForRemove.length > 0) {
            queueImagesDelete();
        }
    });

}

var deleteImage = function(url, callback) {
    $.ajax({
        url: url,
        method: 'DELETE'
    }).done(function(response){
        typeof(callback) == 'function' ? callback(response) : null;
    });
}

queueImagesDelete();

Debido a que la operación síncrona XMLHttpReponse está en desuso, se me ocurrió la siguiente solución que envuelve XMLHttpRequest . Esto permite realizar consultas AJAX ordenadas sin dejar de ser de naturaleza similar, lo que es muy útil para tokens CSRF de un solo uso.

También es transparente, por lo que bibliotecas como jQuery funcionarán sin problemas.

/* wrap XMLHttpRequest for synchronous operation */
var XHRQueue = [];
var _XMLHttpRequest = XMLHttpRequest;
XMLHttpRequest = function()
{
  var xhr   = new _XMLHttpRequest();
  var _send = xhr.send;

  xhr.send = function()
  {
    /* queue the request, and if it's the first, process it */
    XHRQueue.push([this, arguments]);
    if (XHRQueue.length == 1)
      this.processQueue();
  };

  xhr.processQueue = function()
  {
    var call = XHRQueue[0];
    var xhr  = call[0];
    var args = call[1];

    /* you could also set a CSRF token header here */

    /* send the request */
    _send.apply(xhr, args);
  };

  xhr.addEventListener('load', function(e)
  {
    /* you could also retrieve a CSRF token header here */

    /* remove the completed request and if there is more, trigger the next */
    XHRQueue.shift();
    if (XHRQueue.length)
      this.processQueue();
  });

  return xhr;
};

Este es mi & # 8220; tema & # 8221; espacio de nombres:

var Thread = {
    sleep: function(ms) {
        var start = Date.now();

        while (true) {
            var clock = (Date.now() - start);
            if (clock >= ms) break;
        }

    }
};

Y así es como lo llamo:

var d1 = new Date();
console.log('start ' + d1.toLocaleTimeString());
Thread.sleep(10000);
var d2 = new Date();
console.log('end ' + d2.toLocaleTimeString());

Y si miramos la consola obtenemos el siguiente resultado:

start 1:41:21 PM
end 1:41:31 PM
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top