Como faço para que o jQuery execute uma solicitação Ajax síncrona e não assíncrona?

1208

Eu tenho um widget JavaScript que fornece pontos de extensão padrão. Um deles é a beforecreatefunção. Ele deve retornar falsepara impedir que um item seja criado.

Eu adicionei uma chamada Ajax nessa função 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);
  });
}

Mas quero impedir que meu widget crie o item, por isso devo retornar falsena função mãe, não no retorno de chamada. Existe uma maneira de executar uma solicitação AJAX síncrona usando jQuery ou qualquer outra API do navegador?

Artem Tikhomirov
fonte
30
A maneira correta de resolver isso seria reescrever as promessas de uso do ponto de extensão do seu widget. Dessa maneira, seria fácil configurar uma ação assíncrona (como uma solicitação ajax) como beforecreate.
Kos
3
Eu proponho a função Sajak. Temos uma letra T? Sim vanna, me dê 3 T's.
Cody O'Dell
2
@Kos está no local. O XHR de sincronização não é necessário aqui
Michael Westcott
1
Se as pessoas me pedirem um conselho ao iniciar o javascript, digo: adote o caráter assíncrono do javascript, não tente lutar contra isso.
Emmanuel Delay
3
Esta pergunta é como perguntar "como armazenar minhas senhas de usuário em texto sem formatação?" Claro que há uma resposta, mas não faça isso.
Vectorjohn

Respostas:

1165

Na documentação do jQuery : você especifica a opção assíncrona como false para obter uma solicitação Ajax síncrona. Em seguida, seu retorno de chamada pode definir alguns dados antes que a função de sua mãe continue.

Aqui está a aparência do seu código se alterado conforme sugerido:

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
fonte
97
Exatamente, é impossível usar get (), post (), load () para chamadas síncronas. Somente ajax () possui o parâmetro "async", que pode ser definido como "false".
SLA80
39
@ SLA80 Não. Desde o jQuery 1.1: stackoverflow.com/questions/6849686/…
StuperUser
16
@qualidafial meu comentário é direcionado ao comentário incorreto do SLA80; é possível usar get (), post (), load () para chamadas síncronas.
StuperUser
9
o assíncrono false não é mais suportado para jQuery> = 1.8. Consulte api.jquery.com/jQuery.ajax
ken
22
O @ken async: falseainda é suportado no jQuery> = 1.8. A capacidade de usar async: falsecom jqXHR ( $.Deferred) é o que foi preterido. Em outras palavras, é necessário usar as opções mais recentes de sucesso / erro / retorno de chamada completo, em vez dos métodos herdados, por exemplo jqXHR.done(),.
Ben Johnson
255

Você pode colocar a configuração do Ajax do jQuery no modo síncrono chamando

jQuery.ajaxSetup({async:false});

E depois faça suas chamadas Ajax usando jQuery.get( ... );

Em seguida, basta ligá-lo novamente uma vez

jQuery.ajaxSetup({async:true});

Eu acho que funciona da mesma maneira sugerida por @Adam, mas pode ser útil para alguém que deseja reconfigurar sua sintaxe jQuery.get()ou jQuery.post()para a jQuery.ajax()sintaxe mais elaborada .

Sydwell
fonte
56
Essa é uma péssima idéia, por que você a definiria em nível global? E então ser forçado a desmarcá-lo depois?
Juan Mendes
11
Pelo menos, essa é a melhor má idéia. em vez de dizer: "NÃO EXISTE MANEIRA $.ajax()". ;)
Rzassar
136

Excelente solução! Percebi quando tentei implementá-lo que, se eu retornasse um valor na cláusula de sucesso, ele retornaria como indefinido. Eu tive que armazená-lo em uma variável e retornar essa variável. Este é o método que eu criei:

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 em Indy
fonte
16
Isso ocorre porque você estava retornando um valor do retorno de chamada, não de getWhatever. Assim, você retornou nada ou seja, undefineda partir do seu getWhatever.
Lightness Races in Orbit
6
isso está absolutamente errado. Você não pode ter certeza de que uma chamada assíncrona foi finalizada ao retornar 'strReturn'.
Thomas Fritz
61
Esta é uma chamada síncrona (async: false).
James em Indy
85

Todas essas respostas perdem o ponto de que fazer uma chamada do Ajax com async: false fará com que o navegador seja interrompido até que a solicitação do Ajax seja concluída. O uso de uma biblioteca de controle de fluxo resolverá esse problema sem desligar o navegador. Aqui está um exemplo com o 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
fonte
4
As chamadas síncronas são úteis se você deseja montar um conjunto de teste rápido para um back-end REST e preferir a simplicidade do que o inferno de retorno de chamada.
Distortum 19/05
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
fonte
10
Observe que, porém, você pode obter o seguinte: 'O XMLHttpRequest síncrono no encadeamento principal foi descontinuado por causa de seus efeitos prejudiciais à experiência do usuário final.'
Hari
5
@Hari Qual é a maneira de executar uma chamada ajax síncrona usando jQuery sem usar o thread principal?
Jose Nobile
7
Não vejo que essa resposta tenha algo a oferecer, é apenas a resposta aceita, envolvida em uma função e respondida quatro anos depois. É uma coisa ruim aconselhar as pessoas a C + P, porque a função não indica o que faz. "Então getURLvs get? Por que alguém desliga meu navegador?" etc
moopet 26/10/16
27

Nota: Você não deve usar async: falsedevido a estas mensagens de aviso:

A partir do Gecko 30.0 (Firefox 30.0 / Thunderbird 30.0 / SeaMonkey 2.27), solicitações síncronas no segmento principal foram descontinuadas devido aos efeitos negativos para a experiência do usuário.

O Chrome até alerta sobre isso no console:

O XMLHttpRequest síncrono no encadeamento principal foi descontinuado por causa de seus efeitos prejudiciais à experiência do usuário final. Para obter mais ajuda, consulte https://xhr.spec.whatwg.org/ .

Isso pode danificar sua página se você estiver fazendo algo assim, pois pode parar de funcionar a qualquer dia.

Se você quiser fazê-lo de uma maneira que ainda pareça síncrona, mas ainda não bloqueie, use async / waitit e provavelmente também algum ajax baseado em promessas como a nova API de busca

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

O chrome de edição está trabalhando em Desabilitar a sincronização XHR na dispensa de página quando a página está sendo navegada ou fechada pelo usuário. Isso envolve antes de descarregar, descarregar, ocultar página e alterar a visibilidade.

se esse for o seu caso de uso, convém dar uma olhada em navigator.sendBeacon vez

Também é possível que a página desative a sincronização necessária com os cabeçalhos http ou o atributo de permissão do iframe

Sem fim
fonte
Observe que você deve agrupar sua await fetch(url)chamada em um try... catchbloco para detectar erros assíncronos.
Inostia 19/04
4
função foo foi convertido para uma promessa apenas usando a palavra asyncno início, assim que você poderia apenas fazer foo().catch(...)para pegá-lo também
Sem Fim
O Chrome já desabilitou o ajax assíncrono dentro da página, beforeunloadmas depois reverteu a alteração após feedback negativo bugs.chromium.org/p/chromium/issues/detail?id=952452
Alex
26

Lembre-se de que, se você estiver fazendo uma chamada Ajax entre domínios (usando JSONP ) - não poderá fazê-lo de forma síncrona, o asyncsinalizador será ignorado pelo jQuery.

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

Para chamadas JSONP, você pode usar:

  1. Chamada Ajax para seu próprio domínio - e faça a chamada entre domínios do lado do servidor
  2. Altere seu código para funcionar de forma assíncrona
  3. Use uma biblioteca "sequenciador de funções" como o Frame.js (esta resposta )
  4. Bloquear a interface do usuário em vez de bloquear a execução (esta resposta ) (minha maneira favorita)
Serge Shultz
fonte
12

Com async: falsevocê obtém um navegador bloqueado. Para uma solução síncrona sem bloqueio, você pode usar o seguinte:

ES6 / ECMAScript2015

Com o ES6, você pode usar um gerador e a 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

Com o ES7, você pode simplesmente usar o asyc waitit:

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
fonte
3
Observe que você pode apenas passar async functionpara beforecreate beforecreate: async function(....e retire a auto invocado função async
Daniel Krom
12

Usei a resposta dada por Carcione e a modifiquei 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');
    }    
}

Adicionei o dataType de 'JSON' e alterei o .responseText para responseJSON .

Também recuperei o status usando a propriedade statusText do objeto retornado. Observe que esse é o status da resposta do Ajax, não se o JSON é válido.

O back-end deve retornar a resposta em JSON correto (bem formado), caso contrário, o objeto retornado será indefinido.

Há dois aspectos a serem considerados ao responder à pergunta original. Um deles está dizendo ao Ajax para executar de forma síncrona (configurando async: false ) e o outro está retornando a resposta por meio da instrução de retorno da função de chamada, em vez de uma função de retorno de chamada.

Eu também tentei com o POST e funcionou.

Mudei o GET para POST e adicionei dados: 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;
}

Observe que o código acima funciona apenas no caso em que async é falso . Se você configurasse async: true, o objeto retornado jqxhr não seria válido no momento em que a chamada AJAX retornasse, somente mais tarde quando a chamada assíncrona for concluída, mas é tarde demais para definir a variável de resposta .

paulo62
fonte
7

Este é um exemplo:

$.ajax({
  url: "test.html",
  async: false
}).done(function(data) {
   // Todo something..
}).fail(function(xhr)  {
   // Todo something..
});
search9x
fonte
1
Qual é o sentido de usar async falsecom uma solução baseada em promessa. Isso apenas bloqueia o navegador sem nenhum benefício.
Gone Coding
2
@GoneCoding existem situações em que deseja executar código na seqüência exata, nessa situação que podemos usar assíncrono falsa
Nikki
1
@ Nikki: Essa não é absolutamente a maneira de executá-las sequencialmente. Use promessas assíncronas (que as ajaxchamadas já retornam) e .then()ou done()(como já mostra). Bloquear o navegador é uma experiência muito ruim para o usuário. Como eu disse, ter async: falseneste exemplo é uma completa perda de tempo.
Gone Coding
4

Primeiro devemos entender quando usamos $ .ajax e quando usamos $ .get / $. Post

Quando exigimos um controle de baixo nível sobre a solicitação ajax, como configurações de cabeçalho de solicitação, configurações de cache, configurações síncronas etc., então devemos optar por $ .ajax.

$ .get / $. post: quando não exigimos controle de baixo nível sobre a solicitação ajax. Apenas é simples obter / publicar os dados no servidor. É taquigrafia de

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

e, portanto, não podemos usar outros recursos (sincronização, cache etc.) com $ .get / $. post.

Portanto, para controle de baixo nível (sincronização, cache, etc.) Sobre solicitação ajax, devemos optar por $ .ajax

 $.ajax({
     type: 'GET',
      url: url,
      data: data,
      success: success,
      dataType: dataType,
      async:false
    });
Sheo Dayal Singh
fonte
2

esta é minha implementação simples para solicitações ASYNC com jQuery. Espero que isso ajude alguém.

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
fonte
2

Como a XMLHttpReponseoperação síncrona está obsoleta, eu vim com a seguinte solução que envolve XMLHttpRequest. Isso permite consultas AJAX ordenadas enquanto ainda é de natureza assíncrona, o que é muito útil para tokens CSRF de uso único.

Também é transparente, de modo que bibliotecas como o jQuery operam sem 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
fonte
-1

Como a pergunta original era sobre jQuery.get, vale a pena mencionar aqui que (como mencionado aqui ) alguém poderia usá-lo async: falsede uma maneira ideal,$.get() mas evitá- lo, pois o assíncrono XMLHTTPRequestestá obsoleto (e o navegador pode emitir um aviso):

$.get({
  url: url,// mandatory
  data: data,
  success: success,
  dataType: dataType,
  async:false // to make it synchronous
});
Anupam
fonte
1
por que isso é prejudicado?
alias51
-2

Defina asyn:falsena solicitação ajax para torná-lo síncrono.

Aqui está o exemplo de código:

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

Isso pode deixar a tela sem resposta.

Usman Afzal
fonte