how to use the value after the callback - javascript

In my application i'm isolating the networking in a method and performing a callback to get the JSON response , how can i use it afterward ?
getJson(url, acallback);
function getJson(url, callback) {
Ti.API.info(" im here " + url);
var jsonObject;
var xhr = Ti.Network.createHTTPClient();
xhr.setTimeout(3000);
xhr.onload = function () {
var jsonObject = eval('(' + this.responseText + ')');
callback.call(jsonObject)
}
xhr.open("GET", url);
xhr.send();
Ti.API.info(" passed ");
};
function acallback() {
return this;
}
Now my questions is , how can i use the returned output afterward ?

Should look like this:
function getJson(url, callback) {
Ti.API.info(" im here " + url );
var jsonObject, xhr = Ti.Network.createHTTPClient();
xhr.setTimeout(3000);
xhr.onload = function() {
var jsonObject = JSON.parse(this.responseText);
callback(jsonObject)
}
xhr.open("GET" , url);
xhr.send();
Ti.API.info(" passed " );
}
function acallback(json) {
Ti.API.info("data from ajax: " + json);
}
getJson(url , acallback);
Notice that I removed the use of eval since it's bad practice to use that, as it says (here):
The eval function is very fast. However, it can compile and execute
any JavaScript program, so there can be security issues. The use of
eval is indicated when the source is trusted and competent. It is much
safer to use a JSON parser. In web applications over XMLHttpRequest,
communication is permitted only to the same origin that provide that
page, so it is trusted. But it might not be competent. If the server
is not rigorous in its JSON encoding, or if it does not scrupulously
validate all of its inputs, then it could deliver invalid JSON text
that could be carrying dangerous script. The eval function would
execute the script, unleashing its malice.
Also, you better use the var keyword only once per scope.
Edit
If you want to use the resulting json object from outside the scope of the callback, just define the callback in the scope where the json is needed, for example:
var obj = {
x: 4,
doit: function() {
var _this = this;
var callback = function(json) {
alert(_this.x * json.y);
};
getJson(url , callback);
}
}
The json.y part I just made up for the example of course.
2nd Edit
Alternatively, if you want to use the call option, you can do this:
function getJson(url, callback, bindto) {
Ti.API.info(" im here " + url );
var jsonObject, xhr = Ti.Network.createHTTPClient();
xhr.setTimeout(3000);
xhr.onload = function() {
var jsonObject = JSON.parse(this.responseText);
callback.call(bindto, jsonObject)
}
xhr.open("GET" , url);
xhr.send();
Ti.API.info(" passed " );
}
var obj = {
x: 5
}
function myCallback(json) {
alert(this.x * json.y);
}
getJson(url, myCallback, obj);
3rd Edit
If we're on the subject, I recommend using a nice trick, which is used in Prototype, MooTools, jQuery and according to the MDN was Introduced in JavaScript 1.8.5.
Function.prototype.bind = function(scope) {
var _function = this;
return function() {
return _function.apply(scope, arguments);
}
}
You can read the tutorial where I copied to code from.

The code that needs to use the output should be placed in acallback (or placed in a function that's called from acallback).

I believe callback.call(jsonObject) should actually read callback(jsonObject). Once you have invoked an asynchronous function, the way to get its value is via a callback. So, the function you pass in as callback will get the result. Say,
function myCallback(value) {
console.log(value);
}

Related

Creating global VAR in functions

So I'm having trouble with getting a VAR in a function to be global, I have tried the following resources:
What is the scope of variables in JavaScript?
My previous question was marked as a duplicate but after reviewing the link above it did not help with my issue.
Here is my previous question:
So I'm using OpenTok to create a online conferencing tool and need to grab the session details from an API on a different server. I've created a php script on the other server that grabs session information based on the session id provided by a URL parameter. I know that the php script and most of the JavaScript is working correctly because when I console.log data from the parsed JSON it prints the correct information. However when I try to put the variables into the credentials area I get the following error:
ReferenceError: thesession is not defined
Here is the code used to get the JSON from a PHP script on a separate server:
var url_string = window.location.href;
var url = new URL(url_string);
var session = url.searchParams.get("s");
if (session == '') {
window.location.replace("http://www.google.com");
}
var getJSON = function(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'json';
xhr.onload = function() {
var status = xhr.status;
if (status === 200) {
callback(null, xhr.response);
} else {
callback(status, xhr.response);
}
};
xhr.send();
};
getJSON('http://192.168.64.2/api/meeting/?uid=' + session,
function(err, data) {
if (err !== null) {
console.log('Error');
}
var thesession = data.sessionID;
var thetoken = data.token;
console.log(thesession);
console.log(thetoken);
});
let otCore;
const options = {
credentials: {
apiKey: "####",
sessionId: thesession,
token: thetoken
},
And here is a screenshot of the console:
The top console log is "thesession" and the second console log is "thetoken". I have tried looking up the error but can't quite find one with the same usage as mine.
The desired outcome would be that I could using the data from the parsed JSON and use the result as the credentials e.g. data.sessionID which is bound the the VAR thesession.
I know this might be a scope issue, but I'm not sure how I could alter the code to make it work as intended.
Any help would be much appreciated, this one has really got me stumped :)
How would I alter the scope to get the desired function? I have reviewed the link that was given on the previous question, but this didn't help me with my issue.
var thesession = data.sessionID;
Is defined within its execution context, which is the callback function you've passed to getJSON.
One step in the right direction is to reverse the assignment. Assign 'thesession' to the options object within the scope where 'thesession' exists.
const options = {
credentials: {
apiKey: "####",
sessionId: null,
token: thetoken
}
};
getJSON('http://192.168.64.2/api/meeting/?uid=' + session,
function(err, data) {
if (err !== null) {
console.log('Error');
}
var thesession = data.sessionID;
var thetoken = data.token;
console.log(thesession);
console.log(thetoken);
options.credentials.sessionId = thesession;
});
However, it's important to realize that your program is not going to wait for this assignment. It will send the getJSON request, and then continue processing. Your options object won't have a sessionId until the getJSON call finishes and its callback has been invoked.
This would be a good opportunity to delve into Promises, which will help you better understand how to handle the non-blocking nature of javascript.
Your problem is that this line var thesession = data.sessionID is scoped within the function function(err, data) { ... }. In order to allow two functions to use the same variable, you need to make sure that the variable isn't declared somewhere they don't have access to.
It's the difference between this:
function func1() {
var x = 3
}
function func2() {
console.log(x)
}
func1();
func2();
and this:
var x;
function func1() {
x = 3
}
function func2() {
console.log(x)
}
func1();
func2();
Similarly, if you declare var thesession; at the start of your script (or at least outside that other function) then just set it with thesession = data.sessionID, your final part will have access to your variable thesession.
Edit
In context:
var url_string = window.location.href;
var url = new URL(url_string);
var session = url.searchParams.get("s");
var thesession;
var thetoken;
if (session == '') {
window.location.replace("http://www.google.com");
}
var getJSON = function(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'json';
xhr.onload = function() {
var status = xhr.status;
if (status === 200) {
callback(null, xhr.response);
} else {
callback(status, xhr.response);
}
};
xhr.send();
};
getJSON('http://192.168.64.2/api/meeting/?uid=' + session,
function(err, data) {
if (err !== null) {
console.log('Error');
}
thesession = data.sessionID;
thetoken = data.token;
console.log(thesession);
console.log(thetoken);
});
let otCore;
const options = {
credentials: {
apiKey: "####",
sessionId: thesession,
token: thetoken
},
As a side-note - I'd also recommend not using var and instead just using let of const, depending on if you want your variable to be mutable or not.

Make a while loop delay repeating until ajax calls in it are complete

Before I explain what I want to do, here's an MCV of what I'm coding
$("#button").submit(function(event) {
event.preventDefault();
var formData = new FormData(this);
var myString = $('#textarea').val();
var myRegexp = /src="blob:([^'"]+)"/gm;
match = myRegexp.exec(myString);
var inProgress = 0;
while (match != null) {
var xhr = new XMLHttpRequest();
addr = match[1];
xhr.open('GET', 'blob:' + addr, true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
if (this.status == 200) {
var myBlob = this.response;
var data = new FormData();
data.append('file', myBlob);
$.ajax({
url: "uploader.php",
type: 'POST',
data: data,
contentType: false,
processData: false,
beforeSend: function() {
inProgress++;
},
success: function(data) {
myString = myString.replace("blob:" + addr, data);
},
error: function() {
// error
},
complete: function() {
--inProgress;
}
});
} else {
// error
}
};
xhr.send();
match = myRegexp.exec(myString);
}
if (!inProgress) {
formData.set('textarea', myString);
submitForm(formData);
}
});
So, I have a text area and it contains an unknown number of BLOB objects. I first try to find these BLOB objects using regexp and then I upload them to the server using a PHP file called uploader.php. Once the file is uploaded, it will return the URL of the uploaded file and I want to replace the BLOB URL by the URL of the uploaded file in the text area and then submit the final content of the textarea to the server for further processing.
It turns out that when I run the code, only the last instance of the regexp is replaced by its uploaded URL. The others remain as they were. I suspect that this is because the while loop does not wait for the ajax requests to be complete. I had a similar problem when trying to submit the form and I solved it by following the suggestions in this answer but I don't know how to fix this issue this time.
Any idea is appreciated
Update: I tried to make ajax work synchronously but my browser said that it was deprecated and it didn't work.
It seems you don't really need it to be synchronous (and I can't see a case when it's better to make an async action look synchronous), but rather only need it to be sequential.
It is possible to make async actions sequential by the use of callbacks (which are rewritable as Promise and in turn rewritable as async/await methods but I'll keep it simple):
// myString is made global for simplicity
var myString;
function uploadBlob(myBlob, addr, callback) {
var data = new FormData();
data.append('file', myBlob);
$.ajax({
url: "uploader.php",
type: 'POST',
data: data,
contentType: false,
processData: false,
success: function(data) {
// file uploaded OK, replace the blob expr by the uploaded file url(data)
myString = myString.replace("blob:" + addr, data);
callback();
},
error: function() {
// error, the uploaded most likely failed, we leave myString alone
// or alternatively replace the blob expr by empty string
// because maybe we dont want to post blob in the final form submit
// uncomment if needed
// myString = myString.replace("blob:" + addr, "");
callback();
}
});
}
function getBlobAndUpload(addr, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'blob:' + addr, true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
if (this.status == 200) {
var myBlob = this.response;
uploadBlob(myBlob, addr, callback);
} else {
// error, but callback anyway to continue processing
callback();
}
};
xhr.send();
}
function processAddresses(addresses, callback, current) {
var index = current || 0;
// all addresses processed?
if (index >= addresses.length) {
// yes no more address, call the callback function
callback();
} else {
var addr = addresses[index];
// once the get/upload is done the next address will be processed
getBlobAndUpload(addr, function() {
processAddresses(addresses, callback, index + 1);
});
}
}
$("#button").submit(function(event) {
event.preventDefault();
var formData = new FormData(this);
var addresses = [];
// initialize both localString and myString to the content of the textArea
// localString will be used to extract addresses,
// while myString will be mutated during the upload process
var localString = myString = $('#textarea').val();
var myRegexp = /src="blob:([^'"]+)"/gm;
match = myRegexp.exec(localString);
// collect all addresses first
while (match != null) {
addr = match[1];
addresses.push(addr);
match = myRegexp.exec(localString);
}
// initiate sequential processing of all addresses, and
// pass the callback function triggering the form submit
processAddresses(addresses, function() {
// all the successfully uploaded blob exprs in my string should
// be now replaced by the remote file url now (see commented part
// in upload blob error for a variation of the feature
formData.set('textarea', myString);
submitForm(formData);
});
});
So. I said in comments, that you could use async/await, and gave links. Now I am going to try to teach you how to work with promises and XMLHttpRequest.
So first thing. I would use my own 'library' (not really a library, just 3 new command) called PromiseReq which has XMLHttpsRequest that returns Promises.
You would need two functions from it:
sendToServer(config, data) and getServerFile(config). They do what their names implies.(sendToServer is not so good at the time, but I will improve it sometime later). They just use Promises as returns. They work in very easy way. Code # Github
BUT It was designed for my uses only, so it is not very flexible (although I hope I will improve it sometime).
So we need to learn how to use Promises.
Firstly you need to know what Promise is and why do we use it.
Then you can create one like this:
let pmq = new Promise((res,rej)=>{
// PROMISE BODY HERE
});
Here first warning. Promises made that way don't support return as a way to resolve Promise! You have to use res()!
Some functions just return them (such as fetch()) and we can handle them right after calling function.
Now pmq will be our promise.
You can use pmq.then(callback) to handle what will happen if somewhere in promise body is res() call and pmq.catch(callback) to handle what happens when rej() is called. Remember, that .catch(cb) and .then(cb) returns a Promise, so you can safely chain more than one .then() and at the end add .catch() and it will handle rejection from every one of .then()s.
For example:
pmq = fetch("file.txt");
pmq.then(e=>console.log(e.json())).then(console.log).catch(console.error);
There is a big note there.
The order in which this events will fire.
If for example rP() waits 1s than logs "A" then resolves, this code:
let a = rP();
a.then(_=>console.log("B")).catch(console.error);
console.log("C");
will result in:
C
A
B
Becuase of this there is async/await needed to do this.
To do so you have to make an async function with keyword async.
let fn = async ()=>{}
Here is second thing. Async functions ALWAYS return Promise. And that is the second way you can create a promise. You just don't use res(), rej() only return and throw.
Now we can call inside fn():
let a = await rP().then(_=>console.log("B")).catch(console.error);
console.log("C");
and we will get our
A
B
C
Now. How to use it with XMLHttpRequest?
You need to create new Promise with simple XMLHttpRequest inside:
let xmlp = (type, path,data) => return new Promise((res,req)=>{
let xhr = new XMLHttpsRequest();
xhr.open(type, path, true); // true implies that is it asynchronous call
//...
xhr.send(data);
});
And now when to resolve?
XMLHttpRequest has useful event properties and events. The one that is best for our case is onreadystatechange.
You can use it like so:
xhr.onreadystatechange = _=>{
if(xhr.readyState === 4 && xhr.status === 200) // Everything went smoothly
res(xhr.responseText);
else if(shr.readyState === 4 && xhr.status !== 200) // Something went wrong!
rej(xhr.status);
}
And then to get data you can either
Async/Await
// INSIDE ASYNC FUNCTION
let resData = await xmpl("GET", "path.txt", null).catch(console.error);
.then()
let resData;
xmpl("GET", "path.txt", null).then(r=>{
resData = r;
// REST OF WHOLE FUNCTION TO ENSURE THAT resData HAS BEEN SET
})
.catch(console.error);
You can also send data with xmpl().
xmpl("POST", "observer.php", "Data to send to observer.php!")
.then(whatToDoAfterSendFN);
/*
to get such data in PHP you have to use
$post = file_get_contents('php://input');
*/
I know that this answer is a bit messy and stuff, but I didn't have any idea how to write it :P Sorry.

AJAX, pass additional variable to callback and store XMLHTTLRequest.response to variable

I am trying to read a local file on the server with a standard function loadDoc(url, cfunc), then
1) search for a particular string in the file (getLine());
2) if possible, store that line to a variable.
For point 1 I pass a string to the callback.
2) Getting the response is problematic because XMLHTTPRequest is asynchronous. At this moment the error is:
"ReferenceError: xhttp is not defined"
function main(){
var url="data.txt"
var str="1,0,"; //just an example
var myCallBackWithVar = function(){
getLine(str);
};
loadDoc(url, myCallBackWithVar);
//Can I get the line here somehow?
}
function loadDoc(url, cfunc) {
var xhttp=new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4 && xhttp.status == 200) {
cfunc(xhttp);
}
}
xhttp.overrideMimeType('text/plain');
xhttp.open("GET", url, true);
xhttp.send();
}
//Find string with the desired data in txt file
function getLine(str) {
var data=xhttp.responseText;
//Find the line from the txt file
var start=data.indexOf(str);
var end=data.indexOf(";",start);
var line=data.substring(start,end);
return line;
}
data.txt is something like this:
some data here
0,0,9;
1,0,10;
1,1,11;
I have already tried to pass the XMLHTTPRequest objetct getLine(xhttp,str). How to solve points 1 and 2? I'd rather keep it jQuery free for the moment. Thanks
Can I get the line here somehow?
I don't think that's a good idea. You can't be sure that your app will work correctly. XHR is a async function and you should use async architecture.
Here the example how this functionality can be done.
var text; // define global variable
var str = "1,0,"; //just an example
function main(){
var url = "data.txt";
var cb = function (data){
text = getLine(data);
// you can use text var here
// or in anyewhere in your code
}
loadDoc(url, cb);
}
function loadDoc(url, cb) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
cb(xhr.responseText);
}
}
xhr.overrideMimeType('text/plain');
xhr.open("GET", url, true);
xhr.send();
}
//Find string with the desired data in txt file
function getLine(data) {
if(data) {
//Find the line from the txt file
var start = data.indexOf(str);
var end = data.indexOf(";", start);
var line = data.substring(start, end);
return line;
}
}
On complete, you don't need to pass the whole xhttp variable through too the callback function. When you do this:
function getLine(str) {
var data=xhttp.responseText;
xhttp is already out of scope. To fix this, the parameter name would also have to be xhttp.
A better way would be to do :
cfunc(xhttp.responseText);
and then
var data=str
This way, you are passing only what you need as an argument.

Returning ajax data to different functions corresponding to different links

I'm at peak-frustration trying to resolve my mental block re: callbacks. I've read How to return value from an asynchronous callback function? and How to return the response from an Ajax call? (among many other posts), and indeed the latter was helpful with another problem. However what I'm trying to do now is just slightly different and I'm losing my mind trying to adapt it to my code. Maybe my approach is entirely wrong/fundamentally flawed (and not just immature, which I can live with)?
The essence of my problem is that rather than simply returning ajax result to a callback function, I need the resulting json to be available to different functions, corresponding to different events, i.e.:
linkOne.onclick = invoke ajaxReq + getJsonData, then call functionOne with getJsonData result as an argument
linkTwo.onclick = invoke ajaxReq + getJsonData, then call functionTwo with getJsonData result as an argument
linkThree.onclick = invoke ajaxReq + getJsonData, then call functionThree with getJsonData result as an argument
Can't this be done with the link.onclick definition? Why doesn't this work:
linkThree.onclick = functionOne(getJsonData);
Here's my code:
function ajaxReq() {
var request = new XMLHttpRequest();
return request;
}
function getJsonData() {
var request = ajaxReq();
request.open("GET", "/myJSON.json", true);
request.setRequestHeader("content-type", "application/json");
request.send(null);
request.onreadystatechange = function() {
if (request.readyState === 4) {
if (request.status === 200) {
var myJsonString = JSON.parse(request.responseText);
var myJsonArray = myJsonString["An Array in myJSON.json"];
// functionOne(myJsonArray); // callback: what if I need to pass this value to various functions?
return myJsonArray; // ... 'cause this ain't doin' it, and I don't know why
}
}
} // onreadystatechange
} // getJsonData
function functionOne(myJsonArray) {
var myJsonArray = getJsonData(); // why doesn't this work, since, in getJsonData, var request = ajaxReq(); returns an ajax request ?
}
And why, if var request = ajaxReq(); invokes ajaxReq function and returns its result to getJsonData, does var myJsonArray = getJsonData(); in functionOne not do the same?
Any help with this is much appreciated. (p.s. seeking a pure javascript fix, not jQuery.)
svs
As it has been answered in the links you have specified, that we cannot return value from asynchronous call to use it in a synchronous function call. So here is the trick -
Assign all the onclick listeners a common function.
link1.onclick = someCommonfunction;
link2.onclick = someCommonfunction;
link3.onclick = someCommonfunction;
And define the common function like following, which will have json data in the callback, and you can pass that data to any function call.
function someCommonfunction(e) {
/* this is the function which will be finally executed with json data after clicking */
var callback = function(jsonData) {
var myJsonArray = jsonData;
//do some condition check and call functionOne, functionTwo or functionThree
};
getJsonData(callback);
}
I modified getJsonData to call callback with the response data.
function getJsonData(callback) {
var request = ajaxReq();
request.open("GET", "/myJSON.json", true);
request.setRequestHeader("content-type", "application/json");
request.send(null);
request.onreadystatechange = function() {
if (request.readyState === 4) {
if (request.status === 200) {
var myJsonString = JSON.parse(request.responseText);
var myJsonArray = myJsonString["An Array in myJSON.json"];
callback(myJsonArray);
}
}
} // onreadystatechange
} // getJsonData

how to get back the results from HttpRequest inside a method

in my application i want to isolate the Networking in one method , its very common to fetch ajax in my app. so i've put the Ti.Network.createHTTPClient() in a seperate method and i call it with a URL. then it will parse the JSON and return back the result. HOWEVER it always return back a null object.
i'm assuming it retched the end of the method before getting back from the .onload() method
How can i solve that ?
function getJson(url)
{
Ti.API.info(" URL is " + url );
var jsonObject ;
var xhr = Ti.Network.createHTTPClient();
xhr.setTimeout(3000);
xhr.onload = function()
{
var jsonObject = eval('(' + this.responseText + ')');
}
xhr.open("GET" , url);
xhr.send();
Ti.API.info(" passed " );
return jsonObject;
};
You need to set up a callback somewhere in your code like this :
function getJson(url,callback)
{
Ti.API.info(" URL is " + url );
var jsonObject ;
var xhr = Ti.Network.createHTTPClient();
xhr.setTimeout(3000);
xhr.onload = function()
{
callback(jsonObject)
}
xhr.open("GET" , url);
xhr.send();
Ti.API.info(" passed " );
};
function aCallBack(jsonObject){
// the code when the json returns
}
use a callback and give it to your function as a parameter; since it's asynchrone. Like so:
function getJson(url, callback) {
// do your json-ajax stuff
// where you get the response do:
callback(response);
}
function callback(response) {
// do whatever you like with the response
}

Categories

Resources