I don't understand how the responseHandler function below is receiving a response from an API call, after a payment form submission. Here is a brief summary of the code:
var PaymentThing = function(e) {
this["clientCallback"] = e;
return {
createToken: function() {
// HTTP GET URL is generated and then an HTML script element is appended to the page head
var a = "https://example.com/securitytoken?creditcard=xxxx?callback=PaymentThing.callback";
var f = document.createElement("script");
f.src = a;
document.getElementsByTagName("head")[0].appendChild(f)
},
callback: function(e) {
this["clientCallback"](e.status, e.results)
}
}
}
var responseHandler = function(status, response) {
// How does this even work?
console.log(response, status);
}
// Form Submission
jQuery('#checkout_payment_form').submit(function() {
PaymentThing.createToken(responseHandler);
}
So my question is, how does the responseHandler function receive the response from the HTTP GET call? It looks to me like the HTTP call isn't even being made. I only see a URL being added to an HTML script element (with the createToken function), with the URL never even being called/executed (but somehow it is?). Also, how are the (status, response) parameters passed into the responseHandler function when I don't see them being passed into a responseHandler function call?
I don't understand the logic or order of operations here, can anyone please explain what's going on?
EDIT:
Ok I think I see now how the responseHandler function is receiving the status and response parameters. I edited the code above to show there's a callback function in the generated HTTP url. But still, how is that URL being called? It still looks to me like it's inert and being stuck into the HTML of the page and not activating.
I think if you run the responseHandler function and store the result in a variable that can be called as a parameter in the PaymentThing.createToken() it will take it in consideration.
None of the code there calls responseHandler. There is no point in passing it to the createToken method as it completely ignores all the arguments.
It is possible that the script which is added to the page attempts to call a global function named responseHandler, but there's no way to tell without inspecting the script.
Related
I am beginner at JS and was learning AJAX(XHR object) and as you can see the code below. Also, guys if you won't get what I mean please let me know :-)
function easyHTTP() {
this.http = new XMLHttpRequest();
}
// Make an HTTP GET Request
easyHTTP.prototype.get = function(url, callback) {
this.http.open('GET', url, true);
let self = this;
this.http.onload = function() {
if(self.http.status === 200) {
callback(null, self.http.responseText);
} else {
callback('Error: ' + self.http.status);
}
}
this.http.send();
}
const http = new easyHTTP;
http.get('https://jsonplaceholder.typicode.com/posts', function(err, posts) {
if(err) {
console.log(err);
} else {
console.log(posts);
}
});
As you can see the above code uses asynchronous callbacks to handle the retrieved data and then console.log that retrieved data. But I wonder what if instead of using asynchronous callback we will handle the retrieved data and console log it inside onload callback like so:
let self = this;
this.http.onload = function() {
if(self.http.status === 200) {
console.log(self.http.responseText);
} else {
console.log('Error: ' + self.http.status);
}
}
Then just use this code to execute it:
const http = new easyHTTP;
http.get('https://jsonplaceholder.typicode.com/posts')
I just cannot get what is the point of using asynchronous callbacks in XHR when we can wait and handle the retrieved data inside onload callback.
You are probably following some tutorial or where did you get that code?
The reason for supplying a callback to http.get instead of adding the code directly to the onload method is easy: Reusability
What if you want to request from two different URLs and process the data differently? When you embed the processing code into the onload function, you have to implement different get-functions.
The callback on the other hand allows to request from different urls (or even the same) and handle the received data in different ways by specifying different callbacks. This is a typical generalization pattern in javascript.
By the way: The callback is not asynchronous. When onload calls callback, it is called directly, as if the code was at the same place. But what is asynchronous, is the http request itself. It starts executing, when the send method is called. After that javascript just continues executing code. After the request is completed the (anonymous) function assigned to onload is called.
I use Delphi XE7. When my Javascript calls my server function that need around 800ms to read sensor and return data, The browser is unresponsive from the moment I click the button to invoke the Javascript until it finally response returns. I'm using the default Javascript generated by the proxy var serverMethods().getChannel(i); to call into my server function.
Javascript call look like this:
var s = serverMethods().getChannel(i);
serial[i].$sensorlValue.text(s.result.fields.sensorString);
serial[i].$sensorlRealValue.text(s.result.fields.sensor);
serial[i].$sensorStatus.text(s.result.fields.sensorStatus+' '+s.result.fields.name);
serial[i].$sensorError.text(s.result.fields.sensorError);
serial[i].$AVString.text(s.result.fields.AVString);
serial[i].$AVError.text(s.result.fields.AVError);
So by default example there are no Javascript callbacks or promise, so embaracaderom manage somehow to block Javascript from executing until response is back and variable a receive values?
I think about try using jQuery Ajax call on URL, but is there any other solution?
Because serverMethods are generated from proxy but for $ajax I need to manually set each of them. Or maybe I do something wrong here and serverMethods can be used without blocking ?
Thanks.
I found the solution to this problem after researching execution path in ServerFunctionExecutor.js that is called on serverMethods().SOMEAPIFUNCTION()
1. Help and documentation are 0, and google + XE7 questions are 0. So if someone from embaracadero read this PLS MAKE DECENT DOCUMENTATION.
ServerFunctionExecutor.js had on line 263
//async is only true if there is a callback that can be notified on completion
var useCallback = (callback != null);
request.open(requestType, url, useCallback);
if (useCallback)
{
request.onreadystatechange = function() {
if (request.readyState == 4)
{
//the callback will be notified the execution finished even if there is no expected result
JSONResult = hasResult ? parseHTTPResponse(request) : null;
callback(JSONResult, request.status, owner);
}
};
}
So it is posible and NOT DOCUMENTED to use callback for unblocking GUI.
Use it as:
serverMethods().SOMEAPIFUNCTION(par1,par2,.... callback)
If you have Server method defined in delphi code with for example 3 parameters in js 4th parameter is callback:
For this example code now look like this:
serverMethods().getChannel(i,function(a,b,c){
serial.$sensorlValue.text(a.result[0].fields.sensorString);
serial.$sensorlRealValue.text(a.result[0].fields.sensor);
serial.$sensorStatus.text(a.result[0].fields.sensorStatus+' '+s.result.fields.name);
serial[i].$sensorError.text(a.result[0].fields.sensorError);
serial[i].$AVString.text(a.result[0].fields.AVString);
serial[i].$AVError.text(a.result[0].fields.AVError);
});
a is JSON reponse
b is Request status as number 200 or somethin else
c is owner usuali undefined
I've tried Googling this but could not reslove it. It may seem like a really simple issue to others but I'm baffled by it. I have the below code in which I get undefined for the first alert but I still get the correct values in the 2nd alert. BUT if I comment out the first alert (just the line with alert) then the 2nd alert output becomes undefined. Can any one explain why this is and how I may output the 2nd alert correctly without the first one, any Help is greatly appreciated.
function getDetails(ID){
var qArray = [];
$.get('get_Question', {"Id":ID}, function(){})
.success(function(data){
var json = $.parseJSON(data);
qArray.push(json.value1);
qArray.push(json.value2);
});
//First Alert
alert("-> "+qArray[0]);
return qArray;
}
This is the 2nd alert which calls the above method:
var myArray = getDetails(4);
alert("myArray [0]: "+myArray[0]);
You can't return a value, the $.get() call is asynchronous.
You need to defer any operations on qArray until the AJAX call has completed, i.e. inside the callback.
Better yet, use deferred callbacks:
function getDetails(ID) {
return $.get('get_Question', {"Id":ID})
.pipe(function(json) {
return [json.value1, json.value2];
});
}
The .pipe deferred function creates a new promise which will ultimately return the desired array, but only once the AJAX call has completed.
You would then use this like this:
getDetails(ID).done(function(qArray) {
alert("-> " + qArray[0]);
});
Note that $.get() doesn't directly support error callbacks, but with deferred objects you can get access to them:
getDetails(ID).done(function(qArray) {
alert("-> " + qArray[0]);
}).fail(function(jqXHR, textStatus, errorThrown)) {
alert("The AJAX request failed:" + errorThrown);
});
Without this you'd need to build the error handling directly into the getDetails() function and then require some mechanism to tell the rest of the application logic about the error.
NB I've assumed that you don't really need to call JSON.parse() manually - if your web server returns the right Content-Type header then jQuery will do that for you automatically.
Ajax calls happens asynchroniusly, meaning you can't wait for the call to return and get the value. The way to do it is to employ a callback. Your example will become something similar to this:
function getDetails(ID, callback){
$.get('get_Question', {"Id":ID}, function(){})
.success(function(data){
var qArray = [];
var json = $.parseJSON(data);
qArray.push(json.value1);
qArray.push(json.value2);
callback(qArray)
});
}
Calling it will change a bit:
getDetails(4, function (myArray) {
alert("myArray [0]: "+myArray[0]);
});
The First Alert is called before the ajax call is finished, so the variable is still undefined.
This is because the $.get() is done asynchronously. There is no option for $.get() to pass parameter for async calls, so you should use $.ajax() instead and pass a param async: false
The $.get call creates a new asynchronous request for the resource in question.
When you call the first alert it is undefined because the request hasn't been completed yet. Also since you are forced to pause on the alert the request has time to be completed in the background. Enough time for it to be available by the second alert.
The same thing happens when you comment out the first alert. This time the second alert is called before the request is completed and the value is undefined.
You need to either make your requests synchronous or consider continuing execution after receiving the response by using a callback function within the success callback function you have already defined in $.get.
As several others have said, ajax-request are asynchronous. You could however set the async property to false to get a synchronous request.
Example:
function getDetails(ID) {
var result = $.ajax('get_Question', {
async : false,
data : { 'Id' : ID }
});
// do something with the result
return result;
}
I myself would have use a callback function instead beacuse async:false is bad practice and is also deprecated.
You'll need to rewrite $.get to use $.ajax and specify async: false
AJAX is asynchronous: you can't tell when the request will complete. This usually means you need to pass callback methods that will be called with the result of the request when it completes. In your case this would look something like:
function getDetails(ID, callbackFunc){
$.get('get_Question', {"Id":ID}, function(){})
.success(function(data){
var qArray = [];
var json = $.parseJSON(data);
qArray.push(json.value1);
qArray.push(json.value2);
callbackFunc(qarray);
});
}
getDetails(4, function(qArray) {
alert("myArray [0]: "+qArray[0]);
};
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 8 years ago.
I'm using the ajax technique in combination with php and I want to know how to get the return variable from the function that is called by onreadstatechange.
A java function is called onsubmit to then call a php script to verify some things in my database and return true or false depending on the results.
Here is where I run into problems, I want the onsubmit="return snappyajaxfunction()" to return false or true based on the results from the php.
My question is, how do I get the result of false or true from the stateChanged function to become the return for snappyajaxfunction.
I tried doing this for kicks: result = request.onreadystatechange=StateChanged with no luck.
I've also tried saving the responsetxt to a global variable then returning the global variable in snappyajaxfunction with no luck.
snappyajaxfunction()
{
request.onreadystatechange=stateChanged;
request.open("GET",url,true);
request.send(null);
return result;
}
function stateChanged()
{
if (request.readyState==4)
{
return request.responseText;
}
}
The purpose of this is to script is to verify the username / password entered by the user with the username / password stored in my database. If it they don't match it returns false and the form will not submit, if it does the form submits and the user is brought to the welcome page...
You could do:
function stateChanged()
{
if (request.readyState==4)
{
OnStateChanged(request.responseText);
}
}
Where OnStateChanged is a function that act as an event handler...
EDIT: in OnStateChanged you can then submit or not your form based on response
EDIT2: On the server when you check the u/p, if they're right log the user right on and return the status to JavaScript.. then in the JavaScript instead of resubmitting a form, just reload/redirect the page if it is successful or display an alert otherwise...
It is just not possible to return something from a function by an asynchronous operation in it... the original thread of execution is long gone. The asynchronous operation is executed by another thread (If I remember correctly, don't flame).
Thanks
jQuery won't automagically fix the fact that he's trying to base his submission on the results of an asynchronous request.
What you can do is the following:
1) Set the form onsubmit to simply call snappyajaxfunction();
2) In stateChanged, if the readystate is 4, obtain a reference to the form and do the following:
form.onsubmit = function() { return true; };
form.submit();
Basically - Set the onsubmit to always return true and then force it to submit again. For added usability you may want to disable the button causing the submission while waiting for statechanged to happen. And then re-enable it.
feel free to check out this tutorial on ajax.
It will show you how to properly use ajax step-by-step
as inkedmn has suggested, I would recommend using jquery or any of the other js frameworks
The problem is that ajax calls are asynchronous. That means the response is separate from the request. You have to modify your code to handle that. You cannot (should not) make http calls synchronous.
Look at this code. It works, and you'll be able to read the response:
var req = new XMLHttpRequest();
req.open('GET', url, true);
req.onreadystatechange = function (ev) {
if (req.readyState == 4) {
alert(req.responseText);
}
};
req.send(null);
To make your code work you can use closures and pass functions as parameters.2 properties javascript is very good at.
snappyajaxfunction(url, fn)
{
var request...
request.onreadystatechange=function()
{
if (request.readyState==4)
{
fn(request.responseText);
}
};
request.open("GET",url,true);
request.send(null);
}
to use it you can build a function like
var doSomething = function(response){
alert(response);
};
snappyajaxfunction('/country/code/32', doSomething);
The closure allows 'request' to be available within the function that handles the onreadystatechange.
The function 'fn' is passed as a parameter of snappyajaxfunction.
We're using Prototype for all of our Ajax request handling and to keep things simple we simple render HTML content which is then assigned to the appropriate div using the following function:
function ajaxModify(controller, parameters, div_id)
{
var div = $(div_id);
var request = new Ajax.Request
(
controller,
{
method: "post",
parameters: parameters,
onSuccess: function(data) {
div.innerHTML = data.responseText;
},
onFailure: function() {
div.innerHTML = "Information Temporarily Unavailable";
}
}
);
}
However, I occasionally need to execute Javascript within the HTML response and this method appears incapable of doing that.
I'm trying to keep the list of functions for Ajax calls to a minimum for a number of reasons so if there is a way to modify the existing function without breaking everywhere that it is currently being used or a way to modify the HTML response that will cause any embedded javascript to execute that would great.
By way of note, I've already tried adding "evalJS : 'force'" to the function to see what it would do and it didn't help things any.
The parameter is:
evalScripts:true
Note that you should be using Ajax.Updater, not Ajax.Request
See: http://www.prototypejs.org/api/ajax/updater
Ajax.Request will only process JavaScript if the response headers are:
application/ecmascript,
application/javascript,
application/x-ecmascript,
application/x-javascript,
text/ecmascript, text/javascript,
text/x-ecmascript, or
text/x-javascript
Whereas Ajax.Updater will process JS is evalScripts:true is set. Ajax.Request is geared toward data transport, such as getting a JSON response.
Since you are updating HTML you should be using Ajax.Updater anyways.
Does setting evalScripts: true as an option help?
You should be able to do something like this:
div.innerHTML = "<div onclick='someOtherFunctionTocall();'>";
If you need to execute something at the same time as injecting the HTML, can you modify the signature of ajaxModify() by passing another parameter, which will be the javascript function you're going to execute (if it's not null - which let's you keep it optional, as you surely won't want to execute something on EVERY AJAX response).
Just execute a custom my_function() after the ajax response
div.innerHTML=...ajax response text...
my_function()
then execute any function inside the custom my_function()
function my_function() {
function_1()
...
}
Note that my_function() should be somewhere outside the div.innerHTML.
you need to use eval() function to run the javascript in Ajax repose
this can be use full to separate the script and run it
function PaseAjaxResponse(somemixedcode)
{
var source = somemixedcode;
var scripts = new Array();
while(source.indexOf("<script") > -1 || source.indexOf("</script") > -1) {
var s = source.indexOf("<script");
var s_e = source.indexOf(">", s);
var e = source.indexOf("</script", s);
var e_e = source.indexOf(">", e);
scripts.push(source.substring(s_e+1, e));
source = source.substring(0, s) + source.substring(e_e+1);
}
for(var x=0; x<scripts.length; x++) {
try {
eval(scripts[x]);
}
catch(ex) {
}
}
return source;
}
alliteratively for more information see this
http://www.yasha.co/Ajax/execute-javascript-on-Ajax-return/article-2.html