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

1208

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 manera de realizar una solicitud AJAX sincrónica usando jQuery o cualquier otra API en el navegador?

Artem Tikhomirov
fuente
30
La forma correcta de resolver eso sería reescribir las promesas de uso del punto de extensión de su widget. De esta manera, le permitiría configurar fácilmente una acción asincrónica (como una solicitud ajax) como beforecreate.
Kos
3
Propongo la función Sajak. ¿Tenemos una letra T? Sí vanna, dame 3 T's.
Cody O'Dell
2
@Kos es perfecto. Sincronización XHR no se requiere aquí
Michael Westcott
1
Si la gente me pide 1 consejo cuando inicio JavaScript, yo digo: adopta el carácter asincrónico de JavaScript, no intentes luchar contra eso.
Emmanuel Delay
3
Esta pregunta es como preguntar "¿cómo guardo mis contraseñas de usuario en texto plano?" Claro que hay una respuesta, pero no la hagas.
Vectorjohn

Respuestas:

1165

De la documentación de jQuery : usted especifica que la opción asíncrona sea falsa para obtener una solicitud de Ajax sincrónica. Luego, su devolución de llamada puede establecer algunos datos antes de que su función madre continúe.

Así es como se vería su código si se modifica 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
    });
}
Adam Bellaire
fuente
97
Exactamente, es imposible usar get (), post (), load () para llamadas síncronas. Solo ajax () tiene el parámetro "async", que se puede establecer en "false".
SLA80
39
@ SLA80 No. Desde jQuery 1.1: stackoverflow.com/questions/6849686/…
StuperUser
16
@qualidafial mi comentario está dirigido al comentario incorrecto de SLA80; Es posible usar get (), post (), load () para llamadas síncronas.
StuperUser
99
async false ya no es compatible con jQuery> = 1.8. Consulte api.jquery.com/jQuery.ajax
ken
22
@ken async: falsetodavía es compatible con jQuery> = 1.8. La capacidad de usar async: falsecon jqXHR ( $.Deferred) es lo que fue desaprobado. 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
255

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

jQuery.ajaxSetup({async:false});

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

Luego, solo enciéndelo 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()la jQuery.ajax()sintaxis más elaborada .

Sydwell
fuente
56
Esta es una muy mala idea, ¿por qué lo establecerías a nivel global? ¿Y luego ser forzado a desarmarlo después?
Juan Mendes
11
Al menos, esta es la mejor mala idea. en lugar de decir: "NO HAY MANERA EXCEPTO $.ajax()". ;)
Rzassar
136

Excelente solucion! 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;
}
James en Indy
fuente
16
Esto se debe a que estaba devolviendo un valor fuera de la devolución de llamada, no fuera de getWhatever. Por lo tanto no devolvió nada, es decir, undefinedde su getWhatever.
Carreras de ligereza en órbita
66
Esto está absolutamente mal. No puede estar seguro de que una llamada asincrónica ha finalizado al devolver 'strReturn'.
Thomas Fritz
61
Esta es una llamada sincrónica (async: false).
James en Indy el
85

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 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();
}
BishopZ
fuente
44
Las llamadas sincrónicas son útiles si desea armar un arnés de prueba rápida para un back-end REST y prefiere la simplicidad al infierno de devolución de llamadas.
Distortum
50
function getURL(url){
    return $.ajax({
        type: "GET",
        url: url,
        cache: false,
        async: false
    }).responseText;
}


//example use
var msg=getURL("message.php");
alert(msg);
Carcione
fuente
10
Sin embargo, tenga en cuenta que puede obtener esto: 'XMLHttpRequest síncrono en el subproceso principal está en desuso debido a sus efectos perjudiciales para la experiencia del usuario final'.
Hari
55
@Hari ¿Cuál es la forma de realizar una llamada ajax sincrónica usando jQuery sin usar el hilo principal?
Jose Nobile
77
No veo que esta respuesta tenga algo que ofrecer, es solo la respuesta aceptada envuelta en una función y respondida cuatro años después. Es malo aconsejar a las personas que hagan C + P, porque la función no indica lo que hace. "¿Entonces getURLvs get? ¿Por qué uno cuelga mi navegador?" etc.
moopet
27

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

Comenzando con 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:

XMLHttpRequest síncrono en el subproceso 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 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 síncrono pero aún no se bloquea, entonces debe usar async / wait y probablemente también algo de ajax que se base en promesas como la nueva API 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 sincronización XHR en el cierre de página cuando el usuario está navegando o cerrando la página. Esto implica antes de descargar, descargar, ocultar páginas y cambiar la visibilidad.

si este es su caso de uso a continuación, es posible que desee echar un vistazo a navigator.sendBeacon vez

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

Sin fin
fuente
Tenga en cuenta que debe ajustar su await fetch(url)llamada en un try... catchbloque para detectar cualquier error asincrónico.
inostia
44
la función foo se ha convertido en una promesa con solo usar la palabra asyncal principio, por lo que podría hacer foo().catch(...)para atraparla también
Endless
Chrome ya no permitía async ajax dentro de la página, beforeunloadpero luego revirtió el cambio después de comentarios negativos bch.chromium.org/p/chromium/issues/detail?id=952452
Alex
26

Tenga en cuenta que si está haciendo una llamada Ajax entre dominios (utilizando JSONP ), no puede hacerlo de forma sincrónica, asyncjQuery ignorará el indicador.

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

Para llamadas JSONP puede usar:

  1. Llama a Ajax a tu propio dominio y realiza la llamada entre dominios del lado del servidor
  2. Cambia tu código para que funcione de forma asíncrona
  3. Use una biblioteca de "secuenciador de funciones" como Frame.js (esta respuesta )
  4. Bloquee la interfaz de usuario en lugar de bloquear la ejecución (esta respuesta ) (mi forma favorita)
Serge Shultz
fuente
12

Con async: falseusted consigue un navegador bloqueado. Para una solución síncrona sin bloqueo, puede usar lo siguiente:

ES6 / ECMAScript2015

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

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
    })(); 
}
Spenhouet
fuente
3
Tenga en cuenta que puede pasar async functiona beforecreate beforecreate: async function(....y eliminar la función asincrónica auto invocada
Daniel Krom
12

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 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 está diciendo a Ajax que funcione sincrónicamente (configurando async: false ) y el otro le devuelve la respuesta a través de la declaración return de la función de llamada, en lugar de hacerlo en 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 estableciera 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 de respuesta .

paulo62
fuente
7

Este es un ejemplo:

$.ajax({
  url: "test.html",
  async: false
}).done(function(data) {
   // Todo something..
}).fail(function(xhr)  {
   // Todo something..
});
buscando9x
fuente
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.
Se fue la codificación el
2
@GoneCoding hay situaciones en las que queremos ejecutar código en secuencia exacta, en esa situación podemos usar async false
Nikki
1
@ Nikki: Esa no es la forma de ejecutarlos secuencialmente. Use promesas asíncronas (qué ajaxllamadas ya regresan) y .then()o done()(como ya se muestra). Bloquear el navegador es una experiencia de usuario muy pobre. Como dije tener async: falseen este ejemplo es una completa pérdida de tiempo.
Se fue la codificación el
4

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 deberíamos ir 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 el 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
    });
Sheo Dayal Singh
fuente
2

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();
Felipe Marques
fuente
2

Debido a que XMLHttpReponsela operación síncrona está en desuso, se me ocurrió la siguiente solución que se ajusta XMLHttpRequest. Esto permite consultas AJAX ordenadas sin dejar de ser asincrónico, lo cual es muy útil para los 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;
};
Geoffrey
fuente
-1

Dado que la pregunta original era sobre jQuery.get, vale la pena mencionar aquí que (como se mencionó aquí ) se podría usar async: falsede una manera ideal,$.get() pero evitarlo ya que el asíncrono 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
});
Anupam
fuente
1
¿Por qué se rechaza esto?
alias51
-2

Establecer asyn:falseen la solicitud ajax para hacerlo sincrónico.

Aquí está el ejemplo de código:

 $.ajax({
    url: 'some_url',
    type: "GET",
    async: false,
    cache: false,
    dataType: "JSON",
    data: { 
      "p1": v1, 
      "p2": v2
    },
    success: function(result) {
    }  
  }
});

Esto puede hacer que la pantalla no responda.

Usman Afzal
fuente