Funktion zu langsam?

Sprint

Erfahrenes Mitglied
Hallo zusammen,

ich habe hier eine kleine JS Funktion, die zwei Felder ausliest und abgleicht, ob die Steuernummer bereits in der Datenbank vorhanden ist.
Javascript:
function searchUID(rufer){
    var uid = $('#uid').val();
    var kdnr = $('#kdnr').val();
    $.ajax({
        type: 'POST',
        url: 'uid_doppel.php',
        cache: false,
        data: {suchbegriff: uid, kdnr: kdnr},
        success: function (data) {
// alert (data);
            if (data == 'error'){
                if (rufer == '1')
                    $('#uidwarn').html('Diese Steuernummer existiert schon!');
                else
                    alert('Diese Steuernummer existiert schon!');
                return false;
            }else
                return true;
        }
    });
}

Prinzipiell funktioniert es auch, nur wenn ich die Funktion hier bei der abschließenden Formularprüfung aufrufe

Javascript:
var singleuid = searchUID('0');
// alert (singleuid);
if (!singleuid)
    return false;

erhalte ich als Rückgabe immer undefined, egal ob die Steuernummer existiert oder nicht. Die Rückgabe des PHP Scripts ist richtig. Daran kann es also nicht liegen.

Was mir merkwürdig vorkommt ist die Tatsache, daß wenn ich mir per alert() die Rückgabe des PHP Scripts und den Wert von singleuid ausgeben lasse, erst singleuid mit "undefined" ausgegeben wird und dann erst die Rückgabe von PHP. Ich vermute jetzt mal, daß ich immer nur undefined bekomme weil das Ergebnis von searchUID() zu dem Zeitpunkt noch nicht bekannt ist. Aber wie kann ich das verhindern? Ich hatte schon an eine vorgelagerte while() Schleife gedacht, aber aus der kommt er nie mehr raus.

Danke schonmal im Voraus,
Sprint
 
daß ich immer nur undefined bekomme weil das Ergebnis von searchUID() zu dem Zeitpunkt noch nicht bekannt ist.
Diese Vermutung ist genau richtig: Ajax arbeitet per Default asynchron, d. h. eine Anfrage wird an den Server geschickt und nach einer kurzen Zeit trifft die Antwort mit der Ausgabe des PHP-Skriptes ein. Versuchst Du, auf das Ergebnis gleich nach dem Aufruf von $.ajax zuzugreifen, ist die Antwort vom Server noch nicht da und das Ergebnis undefiniert.
Der zweite Grund, warum es nicht funktioniert, ist, dass deine Funktion searchUID keinen Wert zurück liefert, bzw. dass es keine return-Anweisung gibt.

1. Lösungsmöglichkeit: Das Ajax auf synchron umstellen:
http://api.jquery.com/jquery.ajax/
(nach "async") suchen. Und natürlich eine Return-Anweisung in der Funktion searchUID einfügen.

2. Lösungsmöglichkeit: Eine Callback-Funktion an die Funktion searchUID() übergeben, etwa so:
Code:
function searchUID(callback){
    var uid = $('#uid').val();
    var kdnr = $('#kdnr').val();
    $.ajax({
        type: 'POST',
        url: 'uid_doppel.php',
        cache: false,
        data: {suchbegriff: uid, kdnr: kdnr},
        success: callback;
        }
    });
}
searchUID(function(data) {
    // Hier steht die Ausgabe des PHP-Skripts unter data zur Verfügung.
});
 
Mit der Umstellung auf synchron hat es funktioniert. Danke für die schnelle Hilfe!
 
Bitte keine synchronen HTTP-Anfragen in JavaScript im Mainthread (!= Web Worker z. B.) verwenden. Es blockiert z. B. die ganze JS Event Queue, sodass keine weitere JS-Interaktion während der Anfrage geschehen kann, auch kein Reagieren auf ein Buttonklick etwa. Es ist sogar so sehr in Verruf geraten, dass Browserhersteller Support dafür entfernen möchten: https://stackoverflow.com/q/30876093/603003

Eine dritte Möglichkeit und m. E. schönere Möglichkeit als Callbacks ist es, Promises zu verwenden. jQuery bietet da auch eine Abstraktion soweit ich weiß, einfach mal googeln ;) Ansonsten kannst du auch die Fetch API verwenden, die ECMAScript-native Promises anbietet.
 
Nach dem Hinweis von ComFreek habe ich das mal testweise so umgebaut.

Javascript:
function searchUID(callback){
    var uid = $('#uid').val();
    var kdnr = $('#kdnr').val();
    $.ajax({
        type: 'POST',
        url: 'uid_doppel.php',
        cache: false,
        data: {suchbegriff: uid, kdnr: kdnr},
        success: callback
    });
}


searchUID(function(data) {
    if (data == 'error'){
        alert('Diese Steuernummer existiert schon!');
        return false;
    }
});

Leider habe ich hier wieder das selbe Problem wie am Anfang. Es funktioniert zwar, aber die Rückmeldung ist zu langsam bzw. wird nicht abgewartet, so daß das Formular abgesendet wird.
 
Ah, du setzt searchUID in einem submit- oder click-Handler eines Formulars ein? Ob das Event abgebrochen wird, wird am Rückgabewert dieser Handler entschieden. Klassische Rückgabewerte sind immer synchron, ein kleines Beispiel:
Javascript:
function test() {
  setTimeout(function () {
    return 42;
  }, 100);
}

alert(test());
Obiger Code gibt "undefiend" im Alertfenster aus. Man kann die Funktion test ganz stur von oben nach unten lesen und es kommt kein return für test vor. Das return 42 bezieht sich auf die innere anonyme Funktion.
Dass Rückgabewerte synchron sind, ist üblich und ist in jeder Programmiersprache so. Asynchrone Rückgabewerte, wie man sie bei HTTP-Anfragen etwa benötigt, kann man auf zwei Arten abbilden:
  • Callback-Funktion, so wie du es versucht hast
  • Anstatt den eigentlichen Wert, den man asynchron zurückgeben möchte (die HTTP-Antwort), gibt man ein Handle auf etwas zurück. Dieses Handle repräsentiert die asynchrone Antwort. Man schreibt etwa let handle = test(); handle.await(); alert(handle.value); (in Pseudocode).
Aber dazu muss der Code, der die asynchrone Funktion aufruft, auch damit umgehen können. In deinem Fall ist das der interne Code des Browsers, der überprüft, ob der Formularhandler "false" zurückgegeben hat. Diese Schnittstelle kannst du nicht ändern.
Die einzige Lösung bei dir wäre, dass du immer "return false" in deinem Formularhandler (nicht im Callback!) zurückgibst und wenn die HTTP-Anfrage erfolgreich zurückgekommen ist, dass du dann das Formular manuell mit JS triggerst und abschickst.
 
Ich habe jetzt auch noch mit .done() rumprobiert, bekomme da aber erst gar keine Antwort von der searchUID.
Bevor ich da noch mehr Zeit mit vergeude, werde ich jetzt doch die synchrone Abfrage nutzen. Die Seite ist nicht öffentlich und sollte aus irgendwelchen Gründen die Antwort vom PHP Script etwas länger dauern, würde auch alles andere drumherum stehen. Es wäre also kein Hindernis. Und irgendwann in diesem Jahrhundert muß ich mich sowieso an einen Neubau des geamten Pakets machen und da wird sich dann eine andere Lösung finden.

Danke für eure Hilfe!
 
Du warst doch an der Lösung sehr nah dran:
Javascript:
const form = document.getElementById('my-form');
function deinFormHandler(event) {
  event.preventDefault();
  searchUID(function () {
    if (data === 'error') {
      alert('Diese Steuernummer existiert schon!');
    }
    else {
      form.submit();
    }
  });
  return false;
}
Ein bisschen problematisch ist noch, dass wenn searchUID lange braucht, dass dann der Nutzer Textfelder ändern kann und zum Zeitpunkt, in dem searchUID zurückkehrt, das Formular mit den geänderten Werten (unvalidiert) abgesendet wird.
(Wohlgemerkt ist das ein UX-Problem, kein Sicherheitsproblem, denn gefälschte Daten kann sowieso jeder übermitteln -> serverseitiges Validieren ist immer unumgänglich.)

Und irgendwann in diesem Jahrhundert muß ich mich sowieso an einen Neubau des geamten Pakets machen und da wird sich dann eine andere Lösung finden.
Sowas habe ich schon öfters erlebt und am Ende kam es nie zu diesem Neubau, sodass die alte halbkaputte Lösung jahrelang in Benutzung blieb :( Und wenn Browser die Funktionalität entfernen, wird deine Seite einfach stillschweigend (bis auf die Dev-Konsole wohl) nicht mehr funktionieren, von heute auf morgen!
 
Moin ComFreek,

bei deinem Code bin ich jetzt ausgestiegen. Ich habe alle möglichen Kombinationen und Aufrufe ausprobiert, bekomme aber - wenn überhaupt - als Reaktion nur ein "undefined is not an object" auf "event.preventDefault".

Wenn ich das richtig verstehe, muß ich doch deinFormHandler() bei der Prüfung der Felder aufrufen. Oder bin ich da verkehrt?

Ein bisschen problematisch ist noch, dass wenn searchUID lange braucht, dass dann der Nutzer Textfelder ändern kann und zum Zeitpunkt, in dem searchUID zurückkehrt, das Formular mit den geänderten Werten (unvalidiert) abgesendet wird.
(Wohlgemerkt ist das ein UX-Problem, kein Sicherheitsproblem, denn gefälschte Daten kann sowieso jeder übermitteln -> serverseitiges Validieren ist immer unumgänglich.)

Da besteht kein Problem. Wie gesagt ist es keine öffentliche Seite und kann nur von unseren Mitarbeitern erreicht werden. Und wenn die irgendwelchen Müll einschleusen, behindern die sich nur selbst.

Sowas habe ich schon öfters erlebt und am Ende kam es nie zu diesem Neubau, sodass die alte halbkaputte Lösung jahrelang in Benutzung blieb :( Und wenn Browser die Funktionalität entfernen, wird deine Seite einfach stillschweigend (bis auf die Dev-Konsole wohl) nicht mehr funktionieren, von heute auf morgen!

Ich will ja selbst, daß das endlich neu gemacht wird. Zur Not schleuse ich irgendwo eine Pause ein, so daß sie mich irgendwann anbetteln, das endlich neu zu bauen.
 
Wenn ich das richtig verstehe, muß ich doch deinFormHandler() bei der Prüfung der Felder aufrufen. Oder bin ich da verkehrt?
Beim onsubmit-Handler der <form> oder beim onclick-Handler deines Submit Buttons. Kannst du vielleicht mehr Code zeigen? Wie wird "deinFormHandler" aufgerufen?
 
Zurück