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

1276

Tengo un widget de JavaScript que proporciona puntos de extensión estándar. Uno de ellos es la beforecreatefunción. Debería volver falsepara evitar que se cree un elemento.

Agregué 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 regresar falseen la función madre, no en la devolución de llamada. ¿Hay alguna forma de realizar una solicitud AJAX síncrona utilizando jQuery o cualquier otra API en el navegador?

6
  • 35
    La forma correcta de resolver eso sería reescribir las promesas de uso del punto de extensión de su widget. De esta manera, fácilmente le permitiría configurar una acción asincrónica (como una solicitud ajax) como beforecreate. Kos 22/1213 a las 11:32
  • 3
    Propongo la función Sajak. ¿Tenemos una letra T? Sí vanna, dame 3 T's. Cody O'Dell 14/04/2016 a las 0:18
  • 2
    @Kos da en el clavo. No se requiere sincronizar XHR aquíMichael Westcott 9/10/2016 a las 10:47
  • 3
    Si la gente me pide un consejo al iniciar javascript, digo: adopte el carácter asincrónico de javascript, no intente luchar contra eso. Emmanuel Delay 14/12/2017 a las 11:55
  • 4
    Esta pregunta es como preguntar "¿cómo guardo mis contraseñas de usuario en texto sin formato?" Seguro que hay una respuesta, pero no la hagas. Vectorjohn 29/10/18 a las 23:04
1211

De la documentación de jQuery : especificas que la opción asincrónica sea falsa para obtener una solicitud Ajax sincrónica. Luego, su devolución de llamada puede establecer algunos datos antes de que proceda la función madre.

Así es como se vería su código si se cambiara 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
    });
}
13
  • 98
    Exactamente, es imposible usar get (), post (), load () para llamadas sincrónicas. Solo ajax () tiene el parámetro "async", que se puede establecer en "false". SLA80 16 de mayo de 2010 a las 11:41
  • 39
    @ SLA80 No. Desde jQuery 1.1: stackoverflow.com/questions/6849686/…StuperUser 1 de agosto de 2011 a las 17:45
  • 17
    @qualidafial mi comentario está dirigido al comentario incorrecto de SLA80; es posible usar get (), post (), load () para llamadas sincrónicas. StuperUser 8 mar 12 a las 18:00
  • 9
    async false ya no es compatible con jQuery> = 1.8. Consulte api.jquery.com/jQuery.ajaxken 5 de agosto de 2015 a las 8:43
  • 26
    @ken async: falsetodavía es compatible con jQuery> = 1.8. La capacidad de usar async: falsecon jqXHR ( $.Deferred) es lo que quedó en desuso. En otras palabras, hay que utilizar las nuevas opciones de error / éxito / completos de devolución de llamada, en lugar de los métodos heredados, por ejemplo, jqXHR.done(). Ben Johnson 30 oct 2015 a las 14:09
265

Puede poner la configuración Ajax de jQuery en modo síncrono llamando

jQuery.ajaxSetup({async:false});

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

Luego, enciéndelo de nuevo una vez

jQuery.ajaxSetup({async:true});

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

2
  • 58
    Esta es una muy mala idea, ¿por qué la establecería a nivel global? ¿Y luego se verá obligado a desarmarlo después? Juan Mendes 4 de agosto de 2014 a las 18:16
  • 15
    Al menos, esta es la mejor mala idea. en lugar de decir: "NO HAY MANERA EXCEPTO $.ajax()". ;)Rzassar 31 de mayo de 2016 a las 3:22
142

Excelente solucion! Cuando intenté implementarlo, noté que si devolvía un valor en la cláusula de éxito, volvía como 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;
}
3
  • 18
    Eso es porque estaba devolviendo un valor de la devolución de llamada, no de getWhatever. Por lo tanto, no devolvió nada, es decir, undefinedde su getWhatever. Lightness Races in Orbit 11/07/11 a las 23:35
  • 6
    esto es absolutamente incorrecto. No puede estar seguro de que una llamada asincrónica haya finalizado al devolver 'strReturn'. Thomas Fritz 22/02/12 a las 11:30
  • 64
    Esta es una llamada sincrónica (async: false). James in Indy 5/03/12 a las 18:13
88

Todas estas respuestas pierden el punto de que hacer una llamada Ajax con async: false hará que el navegador se cuelgue hasta que se complete la solicitud 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();
}
1
  • 5
    Las llamadas sincrónicas son útiles si desea armar un arnés de prueba rápido para un back-end REST y prefiere la simplicidad al infierno de devolución de llamada. Distortum 19 de mayo de 2014 a las 14:08 h.
52
function getURL(url){
    return $.ajax({
        type: "GET",
        url: url,
        cache: false,
        async: false
    }).responseText;
}


//example use
var msg=getURL("message.php");
alert(msg);
3
  • 10
    Sin embargo, tenga en cuenta que puede obtener esto: "XMLHttpRequest síncrono en el hilo principal está en desuso debido a sus efectos perjudiciales para la experiencia del usuario final". Hari 7 de mayo de 2015 a las 20:09
  • 5
    @Hari ¿Cuál es la forma de realizar una llamada ajax sincrónica usando jQuery sin usar el hilo principal? Jose Nobile 9 de febrero de 2016 a las 21:29
  • 7
    No veo que esta respuesta tenga nada que ofrecer, es solo la respuesta aceptada envuelta en una función y respondida cuatro años después. Es malo aconsejar a la gente que haga C + P, porque la función no indica lo que hace. "¿Entonces getURLvs get? ¿Por qué uno cuelga mi navegador?" etc.moopet 26/10/2016 a las 10:13
29

Nota: No debe usar async: falsedebido a estos mensajes de advertencia:

Starting with Gecko 30.0 (Firefox 30.0 / Thunderbird 30.0 / SeaMonkey 2.27), synchronous requests on the main thread have been deprecated due to the negative effects to the user experience.

Chrome incluso advierte sobre esto en la consola:

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/.

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

Si desea hacerlo de una manera que todavía se siente como si fuera sincrónico pero aún no se bloquea, entonces debería usar async / await y probablemente también algún ajax que se base en promesas como la nueva API de Fetch

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 No permitir la sincronización XHR en el cierre de la página cuando el usuario está navegando o cerrando la página. Esto implica antes de descargar, descargar, ocultar páginas y cambiar de visibilidad.

si este es su caso de uso, es posible que desee echar un vistazo a navigator.sendBeacon en su lugar

También es posible que la página deshabilite la solicitud de sincronización con los encabezados http o el atributo allow de iframe.

3
  • Tenga en cuenta que debe envolver su await fetch(url)llamada en un try... catchbloque para detectar cualquier error asincrónico. inostia 19/04/2017 a las 21:39
  • 4
    la función foo se ha convertido en una promesa con solo usar la palabra asyncal principio, por lo que también podría hacer foo().catch(...)para detectarlaEndless 20 de abril de 2017 a las 8:47
  • 1
    Chrome ya rechazó el ajax asíncrono dentro de la página, beforeunloadpero luego revirtió el cambio después de un comentario negativo bugs.chromium.org/p/chromium/issues/detail?id=952452Alex from Jitbit 20/06/19 a las 10:45
28

Tenga en cuenta que si está haciendo una llamada Ajax entre dominios (utilizando JSONP ), no puede hacerlo de forma síncrona, asyncjQuery ignorará la bandera.

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

Para llamadas JSONP, puede usar:

  1. Llame Ajax a su propio dominio y haga la llamada entre dominios del lado del servidor
  2. Cambie su código para que funcione de forma asincrónica
  3. Use una biblioteca de "secuenciador de funciones" como Frame.js (esta respuesta )
  4. Bloquear la interfaz de usuario en lugar de bloquear la ejecución (esta respuesta ) (mi forma favorita)
14

Usé 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');
    }    
}

He añadido 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 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 no estará definido.

Hay dos aspectos a considerar al responder a la pregunta original. Uno le dice a Ajax que se ejecute 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 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 async 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 es demasiado tarde para establecer la variable de respuesta .

12

Con async: falseusted obtiene un navegador bloqueado. Para una solución sincrónica sin bloqueo, puede utilizar lo siguiente:

ES6 / ECMAScript2015

Con ES6 puede usar un generador y la co-biblioteca :

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 puede 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
    })(); 
}
1
  • 4
    Tenga en cuenta que puede pasar async functiona beforecreate beforecreate: async function(....y eliminar la función asíncrona autoinvocada Daniel Krom 7 de mayo de 2018 a las 16:05
8

Este es un ejemplo:

$.ajax({
  url: "test.html",
  async: false
}).done(function(data) {
   // Todo something..
}).fail(function(xhr)  {
   // Todo something..
});
3
  • 1
    ¿Cuál es el punto de usar async falsecon una solución basada en promesas? Esto solo bloquea el navegador sin ningún beneficio. Gone Coding 5 de septiembre de 2017 a las 18:22
  • 2
    @GoneCoding hay situaciones en las que queremos ejecutar el código en secuencia exacta, en esa situación podemos usar async falseNikki 6 oct 2017 a las 12:05
  • 1
    @Nikki: Eso es absolutamente no la manera de funcionar de forma secuencial. Use promesas asíncronas (las ajaxllamadas ya regresan) y .then()o done()(como ya se muestra). Bloquear el navegador es una experiencia de usuario muy mala. Como dije, tenerlo async: falseen este ejemplo es una completa pérdida de tiempo. Gone Coding 6/10/2017 a las 21:38
5

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 del almacenamiento en caché, la configuración síncrona, etc., entonces deberíamos optar por $ .ajax.

$ .get / $. post: Cuando no requerimos un control de bajo nivel sobre la solicitud ajax. Solo es simple obtener / publicar los datos en el servidor. Es una taquigrafía 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 el control de bajo nivel (sincronización, caché, etc.) sobre la solicitud ajax, deberíamos optar por $ .ajax

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

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();
3

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

También es transparente, por lo que las 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;
};
-1

Dado que la pregunta original trataba sobre jQuery.get, vale la pena mencionar aquí que (como se mencionó aquí ) se podría usar async: falseen un $.get()pero idealmente evitarlo ya que el asincrónico XMLHTTPRequestestá en desuso (y el navegador puede dar una advertencia):

$.get({
  url: url,// mandatory
  data: data,
  success: success,
  dataType: dataType,
  async:false // to make it synchronous
});
0