Getting variable out of a get ajax call - javascript

This problem is actually caused by my not fully understanding jquery.
I have some code that i tried to put in a function to use multiple times.
function actors(query){
$.get("websit.com?title=" + query + "&type=json", function(html){
var result = html;
var obj = eval ("(" + result + ")");
var actor = obj[0].actors;
return actor; //as far as im aware this does nothing
});
return 0; // gets here and returns zero, returning actor here returns undefined
}
The actor variable holds the information i need, however I'm struggling to get the variable out of the function.
Because of the inner function it will run through and get to the return 0;
If i try assign a variable to the inner function it will return an object and not the return variable.
Any solutions or pointers in the right direction would be greatly appreciated.

This question is asked 1000 times monthly. You can not make an asynchronous call act like a synchronous call. That is what the callbacks are for.
You are also using jQuery, there is no need to use eval! Set the right content type and it will parse the data for you.
Basic idea with a callback
function actors(query, callback){
$.getJSON("websit.com?title=" + query + "&type=json", function(data) {
var actor = data[0].actors;
callback(actor);
});
}
function processResults(info){
console.log(info);
}
actors("Something", processResults);

While it's true you can't do just that, I've found that something like this is an acceptable work-around in most cases that I've run in to:
function getMeSomeJSON(query) {
return $.get("websit.com?title=" + query + "&type=json");
}
Which will return the request's response to whatever calls it.
Alternately, you can:
Pass the response to a callback.
Use $.ajax with async: false, however, this is considered bad practice.

Read the documentation. It has some examples, including data type handling. And a simple check should help to determine what to return.

As you have noticed yourself, your ajax response handler executes after the actors function has returned, thus making it rather difficult to return a value. The standard solution would be to return a promise from the actors function. You can read up on promises, or deferred objects, in jquery docs.
This would be my suggestion:
function actors(query){
var actorsPromise = $.Deferred();
$.get("websit.com?title=" + query + "&type=json", function(html){
var result = html;
var obj = eval ("(" + result + ")");
var actor = obj[0].actors;
actorsPromise.resolve(actor);
});
return actorsPromise;
}
actors(/* your query */).done(function(actor) {
//do whatever you need with your actor
});

Related

JavaScript behavior and code check

I created this plunker.
The gist is this code:
var myObj = function (){
return {
getThis: function (callback) {
return callback("this");
}
}
}
var myDidSome = function () {
return {
DoSome: function (text) {
return text + " is cool";
}
};
};
var subObj = function (objG, didSome) {
var txt = objG.getThis(function (text) {
return didSome.DoSome(text);
});
console.log(txt);
}
new subObj(new myObj, new myDidSome);
So to deconstruct this code, view myObj and myDidSome as services (angular ones). I'm most interested in the myObj.getThis, this is supposed to mimick some $http response. So when its finished call my callback function passing in the parameters obtained from $http.
The thing I keep thinking about though is $http is async. Will my txt property in the subOj (think of this as a controller) class be populated correctly when the data gets back, I assume it will be undefined till then?
In my scenario this is completely synchronous, which obviously works.
So, I would like to know whether async will be an issue here? And whether doing this practice of propagating the return of an object is a good one, or doesn't matter, or is bad and I shouldn't do it?
You cannot use return like that with an asynchronous method. You have to do everything within the callback. In your example, the result of didSome.DoSome(text) will never be assigned to txt, not even after the callback has run.
For a simple case like this where you're logging the response, doing it in the callback is an acceptable approach. If you need to do something more complex, however, you should look into promises via $q. These provide a much nicer way to rewrite a long chain of callbacks into a more linear flow.

Async Javascript functions, not returning values

I'm having an issue that is burning my head. Basically this is the scenario:
I have a callController() function, which just simple use the jQuery.load() method to load a controller, after loaded, I can play with the returning parameters.
Now... in one of my controllers there is a validation rule I need to check in order to to allow the user to execute certain functionality, however, I create a function like this:
function myValRule() {
var val = callController('blablabla'),
response = "";
val.done(function(data) {
//my business logic
response = something;
}
return response;
}
As you imagine, response, even if It has a value, it returns undefined, so I set a timeout and console.log() it and now it has a value, but I cannot make to return this value even if I put the return into the setTimeout(). So basically when the method call this function to validate it finds it empty.
Can you point me on some direction to solve this issue?
Remember this is asynchronous! The return response; is long gone by the time .done() is actually called.
if you need the value returned in this fashion, look at either supplying a callback function in your myValRule function (that would be called from within .done()) or maybe look at using the $.Deferred api so you can mimic what callController(...) is doing.
Here's an example of both scenarios (with example call to myValRule):
Callback argument
function myValRule(callback){
var val = callController('blablabla'),
val.done(function(data){
var response = /* something */;
callback(response);
});
}
myValRule(function(response){
// here you have response
});
Using $.Deferred
(I assume it's jQuery since there's a .done call)
function myValRule(){
var df = $.Deferred(),
val = callController('blablabla');
val.done(function(data){
var response = /*something */;
df.resolve(response);
}).fail(df.reject);
return df.promise();
}
myValRule().done(function(response){
// here you have response
});

Return a value from a Javascript closure function?

I'm having trouble returning a value from a closure function in Javascript. Originally I had a return statement in the inside function, but that didn't seem to work. I moved the return statement to the outer function, and it still returns undefined. Shouldn't returnVal be given a value in the inner function before being returned in the outer function?
console.log('Returned: ' + getColumn('CPC'));
function getColumn(header) {
var returnVal = undefined;
ptor.findElements(protractor.By.className('ngHeaderText')).then(function(headers) {
for (var i = 0; i < headers.length; i++) {
(function(i){
headers[i].getText().then(function(headerResult) {
if (headerResult == header) {
console.log('i: ' + i);
returnVal = i;
}
});
})(i);
}
});
return returnVal;
};
Any help would be greatly appreciated!
EDIT: moved the return statement
You can't do a "non-local return" in Javascript, where an inner function returns to an outer function.
That said, your problem here is not the closures, its that you need to code your control flow using the promises. Javascript doesn't "pause" the current function if it emits an async request (like C# or Python would if you do a yield statement) and instead just keeps trucking on. This means that you are calling code to start an async request but going on and returning undefined before the request is complete. What you want to do, is instead immediately return a promise (instead of returning undefined) and do all your control flow inside the promise event handler, using promise-specific methods. (It kind of sucks that you can't use return and for loops as usual but that is a Javascript wart you will have to live with until everyone starts using ES6 generators)
First of all, getColumn should return a promise containing the value of the column. Right now, it does some computation but doesn's return anything:
function getColumn(header){
return ptro.findElements(...);
// ^^^^^
}
Secondly, you usually can't just put async code inside a loop like that. First you need to choose if you want to do the requests in sequence (get the first header, when its done get the second header, etc) or if you want to fire all the header requests ar once and wait for the results. These will have to be coded differently and how you write them will depend if you have to dcode everything by hand and what helper lib you are using for the promises.
For example, if you want to do the processing sequentially, one way is to use recursion instead of a for loop.
then(function(headers) {
function loop(i){
if( i >= headers.length) {
return null; //not found
}else{
return headers[i].getText().then(function(headerResult) {
if (headerResult == header) {
console.log('i: ' + i);
return i;
}else{
return loop(i+1);
}
});
}
}
return loop(0);
});
The code for firing the requests in parallel will vary slightly depending on what promise library you are using. Hopefully you should have some sort of library function for doing that.
Your logic is correct, you can return a value from an inner function to an outer function and then return that value from the outer function. This much simplified example derived from your code shows this:
console.log('Returned: ' + getColumn());
function getColumn() {
var returnVal = undefined;
(function(){
returnVal = 'test';
})();
return returnVal;
};
This results in the following message in the console:
Returned: test
The problem is that you are returning from the function passed to when() but not from the function that this itself is inside

YouTube API doesn't return search query from function?

I notice that when I make str a global variable, instead of making it return from a function like I do in the search function below, it works as it should. However, when I try and return it, it returns undefined. How do I resolve this? I do not want to use globals as I want the search function to be atomicized.
function search(query) {
var str;
// Use the JavaScript client library to create a search.list() API call.
var request = gapi.client.youtube.search.list({part:'snippet',q:query});
request.execute(function(response) {
str = JSON.stringify(response.result);
str = JSON.parse(str);
//console.log(str);
});
return str;
}
I believe your making an async call... That means your function will return str before the callback has been executed!
That is why your getting undefined:
var str; // Undefined!
...
return str; // Returns before your callback executes...
The problem with synchronous calls is you might freeze the client if your waiting for a response and it never arrives!
One way would be to call another method called something like 'gotSearchResults(results)' when your callback executes.
Another dirty way I do not recommend at all is to start a loop, keep running it until str is defined and you got a result.
The best way would properly be to make a callback yourself
function search(query, callback) {
var str;
// Use the JavaScript client library to create a search.list() API call.
var request = gapi.client.youtube.search.list({part:'snippet',q:query});
request.execute(function(response) {
str = JSON.stringify(response.result);
str = JSON.parse(str);
//console.log(str);
callback(str); // Execute your callback with the string as argument!
});
}
search('some query', function(result) {
console.log(result);
});
This is the best way, and I believe the most correct way to achieve what you want!
Hope it helps :)

JavaScript variable value

rpc.getUserInfo(function(result){
userdata = result;
}); //How can i get the value of "userdata"?
I try:
var userdata = "";
rpc.getUserInfo(function(result){
userdata = result;
});
alert(userdata); // it's "undefined"
Time for some assumptions:
RPC usually stands for Remote Procedure Call
This suggests you are performing Ajax
A standard pattern for Ajax functions is to accept a callback function which runs when the call is complete.
Ajax is Asynchronous, so you can't make an Ajax call, wait for the response, then run the next line of the function that made the call.
Rewrite the script so that whatever you want to do when the Ajax data comes back is done in the callback and not in the function that makes the call (which is where it is now).
Wrong:
var userdata = "";
rpc.getUserInfo(function(result){
userdata = result;
});
alert(userdata);
Right:
rpc.getUserInfo(function(result){
alert(result);
});
You haven't said, but I'm guessing that getUserInfo involves an asynchronous ajax call. That being the case, you can't use the value inline, you can only use it in the callback. E.g.:
rpc.getUserInfo(function(result){
var userdata;
userdata = result;
alert(userdata);
});
You just put all of the logic that you would have put after the getUserInfo call inside the callback instead. That sounds difficult or awkward, but with JavaScript it's trivially easy:
function doSomething() {
var foo;
// Do some setup
foo = 42;
// Here's our first asynchronous call; like all good asynchronous calls,
// it accepts a function to call when it's done
triggerAjaxCall("mumble", function(bar) {
// Now the first call is complete, we can keep processing
// Note that we have access to the vars in our enclosing scope
foo += bar;
// Need to do another asynchronous call
triggerSomeOtherAjaxCall(foo, function(nifty) {
// Now the second call is complete and we can keep working with
// all of our stuff
alert("foo: " + foo + "\n" +
"bar: " + bar + "\n" +
"nifty: " + nifty);
});
});
}
Note that doSomething returns before the first callback is called.
Obviously the anonymous functions above are only appropriate if they're an intrinsic part of doSomething; otherwise, move their functionality into separate named functions.

Categories

Resources