2016-05-13 16 views
1

Ich versuche, einen Wert von einem onreadystatechange AJAX-Aufruf zurückzugeben ... Ich habe diese Seite gefunden: stackoverflow link. Ich habe zwar funktioniert, habe aber festgestellt, dass es keinen Unterschied macht, die Fn-Funktion hinzuzufügen oder zu entfernen. Der folgende Code funktioniert:Rückgabewert von onreadystatechange mit Callback

username_is_available(); 

function username_is_available() { 
    var username = document.getElementById('username').value; 

    get_data('username', username, function(returned_value) { 
    if (returned_value == 'true') { 
    document.getElementById('username_err').innerHTML = 'Taken'; 
    } else { 
    document.getElementById('username_err').innerHTML = 'Available'; 
    }; 
    }); 
} 

function get_data(data_type, data, fn) { 
    var xmlhttp = new XMLHttpRequest(); 
    xmlhttp.onreadystatechange = function() { 
    if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { 
    fn(xmlhttp.responseText); 
    } 
}; 
    xmlhttp.open("GET", "availability.php?" + data_type + "=" + data, true); 
    xmlhttp.send(); 
} 

Es funktioniert alles gut, aber das ist nicht mein Ziel, würde ich eine Funktion username_is_available() wie die true zurückgibt, wenn der Benutzername in der Tat zur Verfügung steht. Stattdessen passiert hier eine Aktion (innerHTML wird geändert). Und wenn ich versuche, eine Rückgabe in der anonymen Funktion zu machen, bekomme ich das gleiche Ergebnis, als ob ich es direkt in der onreadystatechange zurückgegeben hätte: var unassigned

Für diejenigen, die gerade auf 'duplicate thread' klicken, ist dies nicht 't ein Duplikat. Ich habe mindestens fünf Stunden auf ihn gewesen

Antwort

2

Leider, da der Prozess zu bestimmen, ob ein Benutzername genommen wird asynchron ist, gibt es keine Möglichkeit, einfach einen Wert von true oder false aus dem Funktionsaufruf zurück. Was Sie tun können, ist etwas Ähnliches wie das, was Sie jetzt haben (Callbacks), die Sprachfunktionen verwenden, die speziell für diesen genauen Zweck entwickelt wurden.

Ein Promise ist eine dieser Funktionen.

Verbrauch würde ungefähr so ​​etwas aussehen:

function username_is_available(username) { 
    return new Promise(resolve => { 
     get_data("username", username, resolve); 
    }); 
} 

function get_data(data_type, data, fn) { 
    var xmlhttp = new XMLHttpRequest(); 
    xmlhttp.onreadystatechange = function() { 
     if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { 
      fn(xmlhttp.responseText == "true"); 
     } 
    }; 
    xmlhttp.open("GET", "availability.php?" + data_type + "=" + data, true); 
    xmlhttp.send(); 
} 

// Usage: 
username_is_available("Ivan").then(available => { 
    let text = available ? "Available" : "Taken"; 
    document.getElementById("username_err").innerHTML = text; 
}); 

Dies beruht auf availablity.phptrue und false als Text zurückkehrt, die in einen Booleschen umgewandelt wird, bevor resolve aufgerufen wird.

In Zukunft, wenn ES7 + async und await Richtlinien verfügbar sind, wird so einfach sein, das Versprechen mit, da dies (man beachte das await Stichwort):

let available = await username_is_available("Ivan"); 
let text = available ? "Available" : "Taken"; 
document.getElementById("username_err").innerHTML = text; 

bearbeiten : Wenn Sie ES6 oder Versprechen nicht verwenden können, ist es zurück zu guten alten Rückrufen!

+1

In Ihrem letzten Beispiel, denke ich, 'genommen' sollte' verfügbar' sein. –

+0

Sie ordnen 'genommen' einen Wert zu und verwenden eine nicht deklarierte Variable 'verfügbar'. Der Variablenname 'genommen' sollte 'verfügbar' sein, weil er einen Boolean von einer Funktion mit dem Ausdruck 'is_available' empfängt. –

+0

@PatrickRoberts Guter Fang, ich wurde in der ersten Antwort ertappt und schrieb hektisch Code in das Textfeld, anstatt einen richtigen Editor und Einfügen wie gewöhnlich. Vielen Dank! – Scott

0

Ich habe mit Beobachtern als Alternative zu Versprechungen gespielt. Ich habe eine an example in plnkr eingerichtet, um zu zeigen, wie es funktionieren kann. Ich denke, in Ihrem Fall es würde wie folgt aussehen:

function Producer() { 
    this.listeners = []; 
} 

Producer.prototype.add = function(listener) { 
    this.listeners.push(listener); 
}; 

Producer.prototype.remove = function(listener) { 
    var index = this.listeners.indexOf(listener); 
    this.listeners.splice(index, 1); 
}; 

Producer.prototype.notify = function(message) { 
    this.listeners.forEach(function(listener) { 
     listener.update(message); 
    }); 
}; 

var notifier = new Producer; 

function get_data(data_type, data) { 
    var xmlhttp = new XMLHttpRequest(); 
    xmlhttp.onreadystatechange = function() { 
     if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { 
      notifier.notify(xmlhttp.responseText); 
     } 
    }; 
    xmlhttp.open("GET", "availability.php?" + data_type + "=" + data, true); 
    xmlhttp.send(); 
} 

var username_is_available = { 
    update: function(returned_value) { 
     var username = document.getElementById('username').value; 
     if (returned_value == 'true') { 
      document.getElementById('username_err').innerHTML = 'Taken'; 
     } else { 
      document.getElementById('username_err').innerHTML = 'Available'; 
     };   
    } 
} 

notifier.add(username_is_available); 
get_data("username", username); 

Notiere die Producer Code wiederverwendbar ist, würden Sie eine neue Instanz für andere Ajax/beobachtbaren Anfragen stellen.