jQuery / .ajax() / Requisições síncronas e alternativa assíncrona
async: false em jQuery.ajax() cria uma requisição síncrona: o fluxo atual espera até concluir. Isso pode bloquear a execução principal e a interface. Em código novo, prefira o modo assíncrono padrão e conecte o trabalho seguinte. Uma segunda requisição que precisa da primeira resposta pode manter a ordem sem bloquear.
Significado de async:false
A API de jQuery.ajax() usa true por padrão. false não acelera: impede continuar após a chamada antes de concluir. O guia MDN de requisições síncronas e assíncronas explica a interface sem resposta.
Em código síncrono antigo, use opções success, error e complete. Desde 1.8, a combinação async:false com processamento Deferred do jqXHR está obsoleta. Isso não torna todo Ajax ou .done() assíncrono obsoleto. .then(), .done() e .fail() abaixo pertencem à alternativa assíncrona.
Se a necessidade é ordem, inicie a próxima requisição após a resposta e retorne-a para conectar. Diferencie trabalho paralelo independente de requisições que precisam de um valor anterior.
Servidor local com atraso e cliente completo
Prepare Python 3, Internet e uma pasta nova. Salve ajax-server.py em UTF-8. Arquivos e três APIs têm a mesma origem. /first fornece id 7, /second usa parent e /slow espera mais para o timeout. Os atrasos são condições intencionais do exercício.
from http.server import ThreadingHTTPServer, SimpleHTTPRequestHandler
from urllib.parse import urlparse, parse_qs
import json
import sys
import time
class Handler(SimpleHTTPRequestHandler):
def do_GET(self):
url = urlparse(self.path)
if url.path not in ("/first", "/second", "/slow"):
return super().do_GET()
time.sleep(2 if url.path == "/slow" else 0.4)
if url.path == "/second":
parent = parse_qs(url.query).get("parent", [""])[0]
data = {"detail": "Details for " + parent}
else:
data = {"id": 7, "name": "Alpha"}
body = json.dumps(data).encode("utf-8")
self.send_response(200)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
try:
self.wfile.write(body)
except (BrokenPipeError, ConnectionResetError):
pass
if __name__ == "__main__":
port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
ThreadingHTTPServer(("127.0.0.1", port), Handler).serve_forever()
Salve ajax-demo.html ali. Use o arquivo oficial completo 4.0.0, não slim sem Ajax. Inícios são desabilitados durante o trabalho e active guarda o jqXHR real atual. Retornar a segunda requisição de .then() faz a cadeia seguinte aguardar também sua conclusão.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Keep request order without blocking</title>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<style>
body { font: 16px/1.6 sans-serif; max-width: 760px; margin: 24px auto; padding: 16px; }
button { font: inherit; margin: 4px; max-width: 100%; }
pre { white-space: pre-wrap; overflow-wrap: anywhere; }
</style>
</head>
<body>
<h1>Sequence asynchronous requests</h1>
<button type="button" class="start" id="run">Run two requests</button>
<button type="button" class="start" id="error">First request fails</button>
<button type="button" class="start" id="timeout">First request times out</button>
<button type="button" id="cancel" disabled>Cancel active request</button>
<button type="button" id="reset">Reset</button>
<p id="heartbeat">heartbeat=0</p>
<pre id="result" aria-live="polite">Ready</pre>
<script>
$(function () {
let active = null, busy = false, lines = [], ticks = 0;
setInterval(function () { ticks++; $("#heartbeat").text(`heartbeat=${ticks}`); }, 100);
function log(line) { lines.push(line); $("#result").text(lines.join("\n")); }
function run(path, timeout) {
if (busy) return;
busy = true; lines = [];
$(".start, #reset").prop("disabled", true); $("#cancel").prop("disabled", false);
log("before");
active = $.ajax({ url: path, dataType: "json", timeout: timeout });
active.then(function (first) {
log("first=" + first.name);
active = $.ajax({ url: "/second", data: { parent: first.id }, dataType: "json", timeout: 1000 });
return active;
}).done(function (second) {
log("second=" + second.detail);
}).fail(function (xhr, textStatus) {
log("failed=" + textStatus + "; HTTP=" + xhr.status);
}).always(function () {
log("complete"); active = null; busy = false;
$(".start, #reset").prop("disabled", false); $("#cancel").prop("disabled", true);
});
log("returned to caller");
}
$("#run").on("click", function () { run("/first", 1000); });
$("#error").on("click", function () { run("/missing.json", 1000); });
$("#timeout").on("click", function () { run("/slow", 300); });
$("#cancel").on("click", function () { if (active) active.abort(); });
$("#reset").on("click", function () { lines = []; $("#result").text("Ready"); });
});
</script>
</body>
</html>
Saída inicial; um temporizador separado aumenta heartbeat a cada 100ms.
Ready
Execute isto na pasta. Se faltar python, confira instalação e caminho. Se 8000 estiver ocupada, use python ajax-server.py 8001 e ajuste o endereço.
python ajax-server.py
Com o servidor ativo, abra http://127.0.0.1:8000/ajax-demo.html, não file://. A Internet carrega a rede de distribuição de conteúdo (CDN) do jQuery. Encerre com Ctrl+C. Executou-se com Windows Edge, 3.7.1, 4.0.0 e servidor Python; só a porta de verificação mudou para uma disponível.
Verificar ordem, falha e cancelamento
- Run two requests mostra primeiro before e returned to caller. heartbeat continua durante a espera.
- Depois vêm first=Alpha, second=Details for 7 e complete. Network mostra /second?parent=7 após a primeira resposta, sem início simultâneo.
- First request fails produz failed=error; HTTP=404 e complete, sem iniciar a segunda.
- First request times out limita o atraso de dois segundos de /slow a 300ms: failed=timeout; HTTP=0. Zero não é um código HTTP enviado pelo servidor.
- Cancel active request cancela a requisição atual: failed=abort; HTTP=0. Após a primeira resposta, active aponta para a segunda.
- Ao concluir, os controles são habilitados. Reset muda apenas a saída para Ready; heartbeat continua. Confira uma tentativa normal após falha.
.always() restaura controles e busy no sucesso ou erro. Limpar só no sucesso poderia deixar botões bloqueados após falha. Diferencie a cadeia do jqXHR cancelável e atualize active a cada $.ajax(). Cancelar não desfaz trabalho já executado pelo servidor.
Observar a ordem síncrona antiga
Com servidor ativo e outras requisições concluídas, execute isto uma vez no Console. Explica async:false apenas com /first local; não substitua por endereço externo lento.
console.log("before synchronous request");
setTimeout(function () { console.log("timer after synchronous request"); }, 0);
$.ajax({
url: "/first",
dataType: "json",
async: false,
success: function (data) { console.log("success=" + data.name); },
error: function (xhr, textStatus) { console.log("failed=" + textStatus); }
});
console.log("after synchronous request");
before synchronous request
success=Alpha
after synchronous request
timer after synchronous request
success precede a linha após a chamada; o temporizador de atraso zero executa depois da chamada síncrona e do código seguinte. Zero milissegundos não interrompe a execução atual. Ocultar aviso de obsolescência não resolve o bloqueio.
Escrever Loading antes não garante tempo para desenhar nem processar temporizadores e entradas. Compare heartbeat assíncrono para diferenciar esperar uma resposta de bloquear o fluxo principal.
Alterar código existente
Mudar só async para true pode ler uma variável externa antes de estar pronta. Mova seu uso para a conclusão ou retorne requisição/Promise para aguardar. Criar uma requisição em .then() sem retornar não faz a cadeia esperar.
Timeout, resposta malformada, falha de rede e cancelamento são distintos. Valide dados em programas reais. Opções de tempo ou cancelamento não removem o bloqueio síncrono. Para envio ou salvamento, adapte novas tentativas às consequências de repetir a operação.




