Wie kann ich jQuery dazu bringen, eine synchrone und keine asynchrone Ajax-Anforderung auszuführen?

1208

Ich habe ein JavaScript-Widget, das Standarderweiterungspunkte bereitstellt. Eine davon ist die beforecreateFunktion. Es sollte zurückkehren false, um zu verhindern, dass ein Element erstellt wird.

Ich habe dieser Funktion mit jQuery einen Ajax-Aufruf hinzugefügt:

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);
  });
}

Ich möchte jedoch verhindern, dass mein Widget das Element erstellt, daher sollte ich falsein der Mutterfunktion und nicht im Rückruf zurückkehren. Gibt es eine Möglichkeit, eine synchrone AJAX-Anforderung mit jQuery oder einer anderen In-Browser-API auszuführen?

Artem Tikhomirov
quelle
30
Der richtige Weg, dies zu lösen, besteht darin, die Verwendungsversprechen für den Erweiterungspunkt Ihres Widgets neu zu schreiben. Auf diese Weise können Sie problemlos eine asynchrone Aktion (wie eine Ajax-Anforderung) als einrichten beforecreate.
Kos
3
Ich schlage die Sajak-Funktion vor. Haben wir einen Buchstaben T? Ja vanna, gib mir 3 T's.
Cody O'Dell
2
@ Kos ist genau richtig. Synchronisierung XHR ist hier nicht erforderlich
Michael Westcott
1
Wenn Leute mich beim Starten von Javascript um einen Rat fragen, sage ich: Umfassen Sie den asynchronen Charakter von Javascript, versuchen Sie nicht, das zu bekämpfen.
Emmanuel Delay
3
Diese Frage ist wie die Frage: "Wie speichere ich meine Benutzerkennwörter im Klartext?" Sicher gibt es eine Antwort, aber tu es nicht.
Vectorjohn

Antworten:

1165

In der jQuery-Dokumentation : Sie geben die asynchrone Option als false an, um eine synchrone Ajax-Anforderung abzurufen. Dann kann Ihr Rückruf einige Daten einstellen, bevor Ihre Mutterfunktion fortgesetzt wird.

So würde Ihr Code aussehen, wenn er wie vorgeschlagen geändert würde:

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
quelle
97
Genau, es ist unmöglich, get (), post (), load () für synchrone Aufrufe zu verwenden. Nur ajax () hat den Parameter "async", der auf "false" gesetzt werden kann.
SLA80
39
@ SLA80 Nein. Seit jQuery 1.1: stackoverflow.com/questions/6849686/…
StuperUser
16
@qualidafial Mein Kommentar zielt auf den falschen Kommentar von SLA80 ab. get (), post (), load () können für synchrone Aufrufe verwendet werden.
StuperUser
9
async false wird für jQuery> = 1.8 nicht mehr unterstützt. Siehe api.jquery.com/jQuery.ajax
Ken
22
@ken async: falsewird in jQuery> = 1.8 weiterhin unterstützt. Die Fähigkeit zur Verwendung async: falsemit jqXHR ( $.Deferred) wurde nicht mehr empfohlen . Mit anderen Worten, man muss die neueren Rückrufoptionen für Erfolg / Fehler / Vollständigkeit anstelle der alten Methoden verwenden, z jqXHR.done().
Ben Johnson
255

Sie können das Ajax-Setup von jQuery durch Aufrufen in den synchronen Modus versetzen

jQuery.ajaxSetup({async:false});

Führen Sie dann Ihre Ajax-Anrufe mit aus jQuery.get( ... );

Dann einfach noch einmal einschalten

jQuery.ajaxSetup({async:true});

Ich denke, es funktioniert genauso wie von @Adam vorgeschlagen, aber es könnte für jemanden hilfreich sein, der seine jQuery.get()oder jQuery.post()die ausgefeiltere jQuery.ajax()Syntax neu konfigurieren möchte .

Sydwell
quelle
56
Dies ist eine wirklich schlechte Idee. Warum sollten Sie sie auf globaler Ebene festlegen? Und dann gezwungen sein, es danach zu deaktivieren?
Juan Mendes
11
Zumindest ist dies die beste schlechte Idee. anstatt zu sagen: "Es gibt keinen Weg außer $.ajax()". ;)
Rzassar
136

Hervorragende Lösung! Als ich versuchte, es zu implementieren, bemerkte ich, dass ein Wert, der in der Erfolgsklausel zurückgegeben wurde, als undefiniert zurückgegeben wurde. Ich musste es in einer Variablen speichern und diese Variable zurückgeben. Dies ist die Methode, die ich mir ausgedacht habe:

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 in Indy
quelle
16
Das liegt daran, dass Sie einen Wert aus dem Rückruf zurückgegeben haben, nicht aus getWhatever. Sie haben also nichts zurückgegeben, dh undefinedvon Ihrem getWhatever.
Leichtigkeitsrennen im Orbit
6
das ist absolut falsch. Sie können nicht sicher sein, dass ein asynchroner Aufruf beendet wurde, wenn Sie 'strReturn' zurückgeben.
Thomas Fritz
61
Dies ist ein synchroner Aufruf (async: false).
James in Indy
85

Alle diese Antworten verfehlen den Punkt, dass ein Ajax-Aufruf mit async: false dazu führt, dass der Browser hängen bleibt, bis die Ajax-Anforderung abgeschlossen ist. Die Verwendung einer Flusssteuerungsbibliothek löst dieses Problem, ohne den Browser aufzulegen. Hier ist ein Beispiel mit 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
quelle
4
Synchrone Anrufe sind praktisch, wenn Sie ein schnelles Testkabel für ein REST-Backend zusammenstellen möchten und die Einfachheit der Rückrufhölle vorziehen möchten.
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
quelle
10
Beachten Sie jedoch, dass möglicherweise Folgendes angezeigt wird: "Synchrones XMLHttpRequest im Hauptthread ist aufgrund seiner nachteiligen Auswirkungen auf die Benutzererfahrung veraltet."
Hari
5
@ Hari Wie kann ein synchroner Ajax-Aufruf mit jQuery ausgeführt werden, ohne den Hauptthread zu verwenden?
Jose Nobile
7
Ich sehe nicht, dass diese Antwort etwas zu bieten hat, es ist nur die akzeptierte Antwort, die in eine Funktion eingewickelt und vier Jahre später beantwortet wurde. Es ist eine schlechte Sache, C + P zu empfehlen, da die Funktion nicht anzeigt, was sie tut. "Also getURLvs get? Warum hängt man meinen Browser auf?" usw.
Moopet
27

Hinweis: async: falseAufgrund dieser Warnmeldungen sollten Sie Folgendes nicht verwenden :

Ab Gecko 30.0 (Firefox 30.0 / Thunderbird 30.0 / SeaMonkey 2.27) wurden synchrone Anforderungen im Hauptthread aufgrund der negativen Auswirkungen auf die Benutzererfahrung abgelehnt.

Chrome warnt sogar in der Konsole davor:

Die synchrone XMLHttpRequest im Hauptthread ist aufgrund ihrer nachteiligen Auswirkungen auf die Benutzererfahrung veraltet. Weitere Hilfe finden Sie unter https://xhr.spec.whatwg.org/ .

Dies könnte Ihre Seite beschädigen, wenn Sie so etwas tun, da es jeden Tag nicht mehr funktioniert.

Wenn Sie es auf eine Weise tun möchten, die sich immer noch so anfühlt, als wäre es synchron, aber immer noch nicht blockiert, sollten Sie async / await und wahrscheinlich auch Ajax verwenden, das auf Versprechungen wie der neuen Fetch- API basiert

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

Chrome bearbeiten funktioniert beim Deaktivieren der Synchronisierung von XHR beim Schließen von Seiten, wenn die Seite vom Benutzer weg navigiert oder geschlossen wird. Dies beinhaltet vor dem Entladen, Entladen, Pagehide und Sichtbarkeitsänderungen.

Wenn dies Ihr Anwendungsfall ist, sollten Sie sich stattdessen navigator.sendBeacon ansehen

Es ist auch möglich, dass die Seite die Synchronisierungsanforderung entweder mit http-Headern oder mit dem Attribut allow von iframe deaktiviert

Endlos
quelle
Beachten Sie, dass Sie Ihren await fetch(url)Aufruf in einen try... catchBlock einschließen sollten , um asynchrone Fehler abzufangen.
Inostia
4
Die Funktion foo wurde in ein Versprechen umgewandelt, indem nur das Wort asyncam Anfang verwendet wurde. Sie können foo().catch(...)es also auch tun , um es zu fangen
Endless
Chrome hat die asynchrone Ajax-Innenseite bereits nicht zugelassen, beforeunloaddie Änderung jedoch nach negativen Rückmeldungen zurückgesetzt. Bugs.chromium.org/p/chromium/issues/detail?id=952452
Alex
26

Denken Sie daran , dass , wenn Sie eine Cross-Domain Ajax - Aufruf zu tun (unter Verwendung JSONP ) - Sie nicht , kann es synchron tun, das asyncwird Flagge von jQuery ignoriert.

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

Für JSONP-Aufrufe können Sie Folgendes verwenden:

  1. Ajax-Aufruf an Ihre eigene Domain - und den domänenübergreifenden Anruf serverseitig
  2. Ändern Sie Ihren Code so, dass er asynchron arbeitet
  3. Verwenden Sie eine "Funktionssequenzer" -Bibliothek wie Frame.js (diese Antwort )
  4. Blockiere die Benutzeroberfläche anstatt die Ausführung zu blockieren (diese Antwort ) (meine Lieblingsmethode)
Serge Shultz
quelle
12

Mit async: falsedir bekommst du einen blockierten Browser. Für eine nicht blockierende synchrone Lösung können Sie Folgendes verwenden:

ES6 / ECMAScript2015

Mit ES6 können Sie einen Generator und die Co-Bibliothek verwenden :

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

Mit ES7 können Sie einfach asyc await verwenden:

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
quelle
3
Beachten Sie, dass Sie einfach übergeben können async function, beforecreate: async function(....um die selbst aufgerufene asynchrone Funktion zu erstellen und zu entfernen
Daniel Krom
12

Ich habe die Antwort von Carcione verwendet und sie so geändert, dass sie JSON verwendet.

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

Ich habe den Datentyp von 'JSON' hinzugefügt und den .responseText in responseJSON geändert .

Ich habe den Status auch mithilfe der statusText- Eigenschaft des zurückgegebenen Objekts abgerufen . Beachten Sie, dass dies der Status der Ajax-Antwort ist und nicht, ob der JSON gültig ist.

Das Back-End muss die Antwort in korrektem (wohlgeformtem) JSON zurückgeben, andernfalls ist das zurückgegebene Objekt undefiniert.

Bei der Beantwortung der ursprünglichen Frage sind zwei Aspekte zu berücksichtigen. Einer weist Ajax an, synchron zu arbeiten (indem async: false festgelegt wird ), und der andere gibt die Antwort über die return-Anweisung der aufrufenden Funktion zurück und nicht in eine Rückruffunktion.

Ich habe es auch mit POST versucht und es hat funktioniert.

Ich habe das GET in POST geändert und hinzugefügt Daten : 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;
}

Beachten Sie, dass der obige Code funktioniert nur in dem Fall , in dem Asynchron ist falsch . Wenn Sie async: true festlegen würden, wäre das zurückgegebene Objekt jqxhr zum Zeitpunkt der Rückkehr des AJAX-Aufrufs nicht gültig, erst später, wenn der asynchrone Aufruf beendet ist. Dies ist jedoch viel zu spät, um die Antwortvariable festzulegen.

paulo62
quelle
7

Dies ist ein Beispiel:

$.ajax({
  url: "test.html",
  async: false
}).done(function(data) {
   // Todo something..
}).fail(function(xhr)  {
   // Todo something..
});
search9x
quelle
1
Was ist der Sinn async falseeiner vielversprechenden Lösung ? Dies sperrt den Browser nur ohne jeglichen Nutzen.
Gone Coding
2
@GoneCoding Es gibt Situationen, in denen Code in exakter Reihenfolge ausgeführt werden soll. In dieser Situation können wir async false verwenden
Nikki
1
@Nikki: Das ist absolut nicht die Art, sie nacheinander auszuführen. Verwenden Sie asynchrone Versprechen (die ajaxAnrufe kehren bereits zurück) und .then()oder done()(wie bereits gezeigt). Das Sperren des Browsers ist eine sehr schlechte Benutzererfahrung. Wie gesagt, async: falsein diesem Beispiel zu haben, ist reine Zeitverschwendung.
Gone Coding
4

Erstens sollten wir verstehen, wann wir $ .ajax verwenden und wann wir $ .get / $. Post verwenden

Wenn wir eine niedrige Kontrolle über die Ajax-Anforderung benötigen, wie z. B. Anforderungen für den Anforderungsheader, Caching-Einstellungen, synchrone Einstellungen usw., sollten wir uns für $ .ajax entscheiden.

$ .get / $. post: Wenn wir keine einfache Kontrolle über die Ajax-Anforderung benötigen. Nur einfach die Daten an den Server senden. Es ist eine Abkürzung von

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

Daher können wir keine anderen Funktionen (Synchronisierung, Cache usw.) mit $ .get / $. post verwenden.

Daher sollten wir uns für eine Low-Level-Kontrolle (Synchronisierung, Cache usw.) Über Ajax-Anforderungen für $ .ajax entscheiden

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

Dies ist meine einfache Implementierung für ASYNC-Anforderungen mit jQuery. Ich hoffe das hilft jemandem.

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

Da der XMLHttpReponsesynchrone Betrieb veraltet ist, habe ich die folgende Lösung gefunden, die umschließt XMLHttpRequest. Dies ermöglicht geordnete AJAX-Abfragen, während sie von Natur aus asyknonisch sind, was für CSRF-Token zum einmaligen Gebrauch sehr nützlich ist.

Es ist auch transparent, sodass Bibliotheken wie jQuery nahtlos funktionieren.

/* 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
quelle
-1

Da die ursprüngliche Frage zu war jQuery.get, ist es erwähnenswert , hier , dass (wie erwähnt hier ) ein könnte verwenden async: falsein ein , $.get()aber idealerweise vermeiden es , da asynchrone XMLHTTPRequestveraltet ist (und der Browser eine Warnung geben kann):

$.get({
  url: url,// mandatory
  data: data,
  success: success,
  dataType: dataType,
  async:false // to make it synchronous
});
Anupam
quelle
1
warum wird das herabgestimmt?
alias51
-2

einstellen asyn:false Ajax-Anfrage ein, um sie synchron zu machen.

Hier ist das Codebeispiel:

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

Dies kann dazu führen, dass der Bildschirm nicht mehr reagiert.

Usman Afzal
quelle