return value from onreadystatechange with callback [duplicate] - javascript

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 6 years ago.
I'm trying to return a value from an onreadystatechange AJAX call... I found this page : stackoverflow link. I though I had it working but realised that it made no difference to add or remove the fn function. The following code works :
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();
}
It all works fine but that's not my goal, I would like a function username_is_available() that returns true if the username is indeed available.
Instead, here I an action happens (innerHTML is changed). And if I try and do a return in the anonymous function I get the same result as if I had returned it from directly inside the onreadystatechange : var unasigned

Unfortunately, since the process to determine if a username is taken is asynchronous, there is no way to simply return a value of true or false from the function call. What you can do is set up something similar to what you have now (callbacks) using language features specifically designed for this exact purpose.
A Promise is one of these features.
Usage would look something roughly like this:
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;
});
This relies on availablity.php returning true and false as text, which is converted to a Boolean before resolve is called.
In the future, when ES7+ async and await directives are available, using the promise will be as simple as this (note the await keyword):
let available = await username_is_available("Ivan");
let text = available ? "Available" : "Taken";
document.getElementById("username_err").innerHTML = text;
Edit: If you can't use ES6 or promises, it's back to good ol' callbacks!
function username_is_available(username, callback) {
get_data("username", username, callback);
}
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", function(available) {
var text = available ? "Available" : "Taken";
document.getElementById("username_err").innerHTML = text;
});

I've been playing around with observers as an alternative to promises. I set up a an example in plnkr to show how it can work. I think in your case it would look like this:
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);
Note the Producer code is reusable, you would make a new instance for other ajax/observable requests.

Related

Unable to pass variables to onreadystatechange

This is the current source code:
var xhttp = new XMLHttpRequest();
function passVars(var1, var2, var3) {
if (var1.readyState == 4) {
if (var1.status == 200) {
var data = var1.responseText;
if (data) {
playSuccess();
classSwitch(data, var2, var3);
} else {
playFailure();
alert("Error: returned status code " + var1.status + " " + var1.statusText);
}
}
}
}
xhttp.onreadystatechange = function() {
passVars(xhttp, "tab", "p1234");
};
xhttp.open("POST", "index.php", true);
xhttp.send(formData); //formdata is a POST send to PHP's backend which returns responseText = 0 or 1 for var data
function classSwitch(state, sOrigin, stateButtonID) {
if (sOrigin === "tab") {
Monitored = state;
if (state === "1") {
if (document.getElementById("setmonitoring").classList.contains("productmonitoringdisabled")) {
document.getElementById("setmonitoring").classList.remove("productmonitoringdisabled");
}
document.getElementById("setmonitoring").classList.add("productmonitoringenabled");
}
if (state === "0") {
if (document.getElementById("setmonitoring").classList.contains("productmonitoringenabled")) {
document.getElementById("setmonitoring").classList.remove("productmonitoringenabled");
}
document.getElementById("setmonitoring").classList.add("productmonitoringdisabled");
}
}
if (sOrigin === "issues") {
if (state === "1") {
if (document.getElementById(stateButtonID).classList.contains("productmonitoringdisabled")) {
document.getElementById(stateButtonID).classList.remove("productmonitoringdisabled");
} else document.getElementById(stateButtonID).classList.add("productmonitoringenabled");
}
if (state === "0") {
if (document.getElementById(stateButtonID).classList.contains("productmonitoringenabled")) {
document.getElementById(stateButtonID).classList.remove("productmonitoringenabled");
} else document.getElementById(stateButtonID).classList.add("productmonitoringdisabled");
}
}
}
Tried a lot of ways to pass them using mainly SO anwsers and each time var2 and var2 are undefined. This is used in an inventory control system, early alpha version. The idea is to pass element's id to change button class when the backend returns product's current monitoring state
Any ideas how to pass those variables? Thanks in advance
Your structure is no good. You can't pass xhttp like that in a asynchronous call. Rewrite it directly like this...
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4) {
if (xhttp.status == 200) {
var data = xhttp.responseText;
if (data) {
playSuccess();
classSwitch(data, 'tab', '1234');
} else {
playFailure();
alert("Error: returned status code " + xhttp.status + " " + xhttp.statusText);
}
}
}
};
Below is a minimal example of how to pass parameters to the onreadystatechange callback function. You've overcomplicated it a bit - there's no need to pass your xhttp variable to the callback either since it's already available in scope.
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://developer.mozilla.org/");
xhr.send();
xhr.onreadystatechange = function() {
callback("tab", "1234");
};
function callback(var1, var2) {
console.log(`var1 = ${var1}`);
console.log(`var2 = ${var2}`);
//Do your xhr.readyState and xhr.status checks in this function
console.log(`xhr.readyState = ${xhr.readyState}`);
console.log(`xhr.status = ${xhr.status}`);
}
used
function passVars(var1, var2, var3) {
xhr.onreadystatechange = function(e) {}
}
the "e" in the function bracket will call back all var you have before in the function passVars

I tried to create AJAX function but it shows noting in output

I tried to create AJAX function but it shows noting in output
var ajaxObj = function(url, meth, data = "") {
var x = new XMLHttpRequest();
x.onreadystatechange = function() {
if (x.readyState == 4 && x.status == 200) {
this.responseAjax = this.responseText;
}
}
x.open(meth, url, true);
x.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
x.send(data);
}
function showHint(str) {
var xhttp = new ajaxObj("gethint.php?q=" + str, "GET");
if (str.length == 0) {
document.getElementById("txtHint").innerHTML = "";
return;
}
document.getElementById("txtHint").innerHTML = xhttp.responseAjax;
}
<!DOCTYPE html>
<html>
<body>
<h3> Start typing a name in the input field below :</h3>
<form action="">
First Name : <input type="text" id="txt1" onkeyup="showHint(this.value)">
</form>
<p>Suggestions:
<sapn id="txtHint"></sapn>
</p>
</body>
</html>
I tried to get suggested Names from gethint.php file when user starts typing into text box.
But it seems that responseAjax gets value after showHint() call please help me.
You need to handle the AJAX request asynchronously. In your showHint function when you call
document.getElementById("txtHint").innerHTML = xhttp.responseAjax;
the AJAX call has not yet returned, so the xhttp.responseAjax object is not yet defined. You need to wait to handle the response once it arrives. You can pass a callback function in to the ajaxObj definition, and the object will call that function when it gets its response.
var ajaxObj = function(url, meth, callback, data = "") {
var x = new XMLHttpRequest();
x.onreadystatechange = function() {
if (x.readyState == 4 && x.status == 200) {
// we're ready to handle the response data now
callback(x.responseText);
}
}
x.open(meth, url, true);
x.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
x.send(data);
}
// Callback function to be invoked when AJAX is complete
function fillTxtHint(responseHtml)
{
document.getElementById("txtHint").innerHTML = responseHtml;
}
function showHint(str) {
if (str.length == 0) {
document.getElementById("txtHint").innerHTML = "";
}
// Pass in the callback function to be invoked when AJAX has returned
ajaxObj("gethint.php?q=" + str, "GET", fillTxtHint);
}
this inside onreadystatechange's handler is an instance of XMLHttpRequest so the line this.responseAjax = this.responseText is adding a field to the XMLHttpRequest object and setting its value to another field in the same object. This is completely redundant. In showHint, xhttp is an instance of ajaxObj and there is no responseAjax field ever defined for this object. You can directly set innerHTML of the element that shows suggestion inside the handler of onreadystatechange like the following:
function getSuggestions (meth, data) {
meth = meth.toUpperCase();
var params = "q=" + data;
var url = (meth == "GET") ? "gethint.php?" + params : "gethint.php";
var elem = document.getElementById("txtHint");
if (data.length == 0) {
elem.innerHTML = "";
return;
}
var x = new XMLHttpRequest();
x.onreadystatechange = function() {
if (x.readyState == 4 && x.status == 200) {
elem.innerHTML = this.responseText;
}
}
x.open(meth, url, true);
x.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
if (meth == "GET") {
x.send();
} else {
x.send(params);
}
}
and showHint becomes:
function showHint(str) {
getSuggestions ("GET", str);
}

Returning the return value from inner function to parent function- Javascript, Dynamics crm

Will the below code return the boolean value from the innner function to the parent function displayButton()? The parent function is called on click of a button in dynamics CRM. The function should return a boolean value depending if the a case is selected and the selected is active or resolve.
//function called on click of a button in ms crm.
function displayButton()
{
var Obj = parent.Xrm.Page.getAttribute("regardingobjectid");
var ObjValue = Obj.getValue();
//parent.Xrm.Utility.alertDialog(" Value: " + ObjValue);
if (ObjValue == null)
return false;
//else
// parent.Xrm.Utility.alertDialog(" Hi");
var EntityType = ObjValue[0].entityType;
var Guid = ObjValue[0].id;
var id = Guid.slice(1, -1);
//parent.Xrm.Utility.alertDialog(" Guid: " + id);
//Checking if regarding field is selected a case lookup value
if (EntityType == "incident")
{
var req = new XMLHttpRequest();
req.open("GET", parent.Xrm.Page.context.getClientUrl() + "/api/data/v8.2/incidents(" + id + ")?$select=statecode", true);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("Prefer", "odata.include-annotations=\"*\"");
req.onreadystatechange = function ()
{
if (this.readyState === 4)
{
req.onreadystatechange = null;
if (this.status === 200)
{
debugger;
var result = JSON.parse(this.response);
//checking if selected case is active or resolved.
var statecode = result["statecode"];
var statecode_formatted = result["statecode#OData.Community.Display.V1.FormattedValue"];
if (statecode_formatted == "Active") {
return true;
}
else if (statecode_formatted == "Resolved")
return false;
else {
return false;
}
}
else
{
parent.Xrm.Utility.alertDialog("Zero");
}
}
};
req.send();
}
else {
return false;
}
}
No. If you want to access the value returned by your asynchronous XmlHttpRequest, either you'll need to put your logic in the if (this.status === 200) scope or provide a callback.
Your XMLHttpRequest is asynchronous because of the true parameter in this line:
req.open("GET", parent.Xrm.Page.context.getClientUrl() + "/api/data/v8.2/incidents(" + id + ")?$select=statecode", true);
To provide a callback, separate out your code into two functions:
function getCaseState(id, callback) {
var req = new XMLHttpRequest();
req.open("GET", parent.Xrm.Page.context.getClientUrl() + "/api/data/v8.2/incidents(" + id + ")?$select=statecode", true);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("Prefer", "odata.include-annotations=\"*\"");
req.onreadystatechange = function () {
if (this.readyState === 4) {
req.onreadystatechange = null;
if (this.status === 200) {
var result = JSON.parse(this.response);
var statecode = result["statecode"];
var statecode_formatted = result["statecode#OData.Community.Display.V1.FormattedValue"];
callback(statecode_formatted);
}
}
};
req.send();
}
Then call getCaseState, passing it the id of your incident and a function to call once the request is ready:
// called on click of a button in CRM.
function displayButton() {
var regarding = parent.Xrm.Page.getAttribute("regardingobjectid").getValue();
var entityType = regarding[0].entityType;
var regardingId = regarding[0].id.slice(1, -1);
// Check if regarding is an active case.
if (entityType == "incident") {
getCaseState(regardingId, function(state) {
var isActive = state === "Active";
if (isActive) {
// TODO
}
});
}
}
The function passed in the code above is anonymous -- you should separate it out further and name it.
The short answer is No. The inner function gets defined and assigned to req.onreadystatechange. displayButton() never calls the inner function. So it won't be executed in this context and therefore won't return a value.
Probably the this could be a good dig deeper into functions in JS: https://www.w3schools.com/js/js_function_definition.asp
First of all you have pasted way too much code. This is Stack Overflow, you should provide Minimal, Complete, and Verifiable example
The only way to do that without changing your code too much would be to change your request to synchronous as suggested already and assign result to some variable defined outside of the callback. Something like that:
function displayButton(){
var result = false; //or true, depends what you want as default
//some code
//change it to synchonous request!!
var req = new XMLHttpRequest();
req.open("GET", parent.Xrm.Page.context.getClientUrl() + "/api/data/v8.2/incidents(" + id + ")?$select=statecode", false);
//...
req.onreadystatechange = function () {
if (this.readyState === 4) {
req.onreadystatechange = null;
if (this.status === 200) {
//ommiting more code to get to the clue
//...
//...
if (statecode_formatted == "Active") {
result = true;
}
else if (statecode_formatted == "Resolved")
result = false;
else {
result = false;
}
}
}
};
req.send();
return result;
}
I don't want to change your whole code, because you pasted way too much of it, but I'm sure you've got the idea. As suggested in another answer, you should move your calling function to a separate funtion with callback and assign your "result" in this callback. Callback will be run synchronously, so the function will return your "result" with proper value.
Your XMLHttpRequest is asynchronous here. So you can not get Response immediately so execution will not end immediately.
i solved this problem using
setTimeout(function () {
},550);
i think u can try in following way.
here change i did is i kept XMLHttpRequest in separate function i.e, getResult(id) and calling that function from parent function i.e, displayButton().
And insted of returning value from child function, i'm setting that Boolean value to Global variable i.e, var boolValue;
And in parent function before returning a value i'm waiting for 550 mil sec
, so that asynchronous execution gets over.
below is the code snippet.
Parent function
var boolValue;
//function called on click of a button in ms crm.
function displayButton() {
var Obj = parent.Xrm.Page.getAttribute("regardingobjectid");
var ObjValue = Obj.getValue();
//parent.Xrm.Utility.alertDialog(" Value: " + ObjValue);
if (ObjValue == null)
return false;
//else
// parent.Xrm.Utility.alertDialog(" Hi");
var EntityType = ObjValue[0].entityType;
var Guid = ObjValue[0].id;
var id = Guid.slice(1, -1);
//parent.Xrm.Utility.alertDialog(" Guid: " + id);
//Checking if regarding field is selected a case lookup value
if (EntityType == "incident") {
getResult(id);
setTimeout(function () {
if(boolValue == true || boolValue == false)
{
return boolValue;
}
}, 550);
}
else {
return false;
}
}
child function
function getResult(id)
{
var req = new XMLHttpRequest();
req.open("GET", parent.Xrm.Page.context.getClientUrl() + "/api/data/v8.2/incidents(" + id + ")?$select=statecode", true);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("Prefer", "odata.include-annotations=\"*\"");
req.onreadystatechange = function () {
if (this.readyState === 4) {
req.onreadystatechange = null;
if (this.status === 200) {
var result = JSON.parse(this.response);
//checking if selected case is active or resolved.
var statecode = result["statecode"];
var statecode_formatted = result["statecode#OData.Community.Display.V1.FormattedValue"];
if (statecode_formatted == "Active") {
boolValue = true;
}
else if (statecode_formatted == "Resolved")
boolValue = false;
else {
boolValue = false;
}
}
else {
parent.Xrm.Utility.alertDialog("Zero");
}
}
};
req.send();
}
This way solved my problem.

Return datas inside XMLHttpRequest.onreadystatechange [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 7 years ago.
I have this function :
function getDatas() {
var xmlhttp = new XMLHttpRequest();
var response = null;
xmlhttp.open("POST", "getdatas.php", true);
xmlhttp.onreadystatechange = function() {
if(xmlhttp.readyState === 4) {
if(xmlhttp.status === 200) {
response = xmlhttp.responseText;
return response;
}
else {return xmlhttp.statusText;}
}
};
xmlhttp.send(null);
}
The response is in a JSON format and correctly filled.
How to return the response for another use like :
var datas = getDatas();
UPDATE :
After the callback I have this :
function AppViewModel() {
var _self = this;
getDatas(function(error, result) {
if(error) {
alert(error);
} else {
_self.datas = result;
console.log(_self.datas);
}
});
console.log(_self.datas);
}
The first console.log(_self.datas); works well but the second is undefined.
AJAX is asynchronous, so you don't return any values from such functions, you use callbacks and use the value inside the callback:
function getDatas(callback) {
var xmlhttp = new XMLHttpRequest();
var response = null;
xmlhttp.open("POST", "getdatas.php", true);
xmlhttp.onreadystatechange = function() {
if(xmlhttp.readyState === 4) {
if(xmlhttp.status === 200) {
response = xmlhttp.responseText;
callback(null, response);
}
else {
callback(xmlhttp.statusText, null);
}
}
};
xmlhttp.send(null);
}
and then when you call the getDatas function you provide a callback which will be invoked when the AJAX request finishes and it will either contain the result or an error:
getDatas(function(error, result) {
if (error) {
alert('An error occurred while retrieving data: ' + error);
} else {
// Use the result variable here
}
});

How can I change this variable with ajax?

I'm curious as to why this isn't working, here's the code:
function Ajax(sUrl, fCallback) {
var url = sUrl || '';
var callback = fCallback || function () {};
var xmlhttp = (function () {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else if (window.ActiveXObject) {
try {
return new ActiveXObject("Msxml2.XMLHTTP.6.0");
} catch (e) {
try {
return new ActiveXObject("Msxml2.XMLHTTP.3.0");
} catch (err) {
return new ActiveXObject("Microsoft.XMLHTTP");
}
}
} else {
return null;
}
}());
this.setUrl = function (newUrl) {
url = newUrl;
};
this.setCallback = function (func) {
callback = func;
};
this.request = function (method, data) {
if (xmlhttp === null) { return false; }
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState === 4) {
callback(xmlhttp.status, xmlhttp.responseXML, xmlhttp.responseText);
}
};
data = data || '';
data = encodeURIComponent(data);
if ((/post/i).test(method)) {
xmlhttp.open('POST', url);
xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xmlhttp.send(data);
} else {
var uri = data === '' ? url : url + '?' + data;
xmlhttp.open('GET', uri);
xmlhttp.send();
}
return true;
};
return this;
}
var ajax = new Ajax(''); // sets the url, not necessary for this demonstration
var changed = false;
function change() {
changed = true;
}
function foo() {
ajax.setCallback(change);
ajax.request();
alert(changed);
}
foo();
There is a fiddle here: http://jsfiddle.net/dTqKG/
I feel like the change function would create a closure that would indeed change the changed variable. Does anyone know what's going on?
The ajax.request(); will return before change() is called. That is the async nature of the AJAX calls, and the reason why you need the callback as opposed to just getting return value from send() method.
Other than that there might be some other issues in the code. I question why wouldn't you use one of the many AJAX frameworks readily available instead of writing your own.

Categories