I have a piece of code that submits a GET request to another part of my website in a function.
function getStatus(ID) {
$.get('/api/'+ID+'/info', function(statusCallback) {
return statusCallback;
});
}
console.log(getStatus(ID));
What I would expect this code to return and then log would be the information that I need.
What I actually get in console log is
undefined
What can I do to get the actual result?
You're doing async operation. In your case using callback. If you want to print statusCallback, you have to console.log it like Christos mentioned.
Also
console.log(getStatusId())
will return undefined because it's default value returned from the function that has no explicit return. Your function has no return statement, it's only calling some async method.
Btw, try promises instead of callbacks if you can ;)
With ES7 you can use async await
async function getStatus(ID) {
$.get('/api/'+ID+'/info', function(statusCallback) {
// This is executed in the future, but await knows how to deal with it
return statusCallback;
});
}
console.log(await getStatus(ID));
They are a good practice to get started with, because the code gets a lot easier to read.
You need to change your functions as below:
function getStatus(ID) {
$.get('/api/'+ID+'/info', function(statusCallback) {
console.log(statusCallback);
});
}
The function you pass as the second parameter of your get call is called success callback. It will be called, once the get request that is issued on each call of the getStatus function completes successfully. Only then you have access to what the server returned and you can fairly easy access it as above.
Update
If you want to return the data that server sends you have to declare first a variable
function getDataById(ID){
function callback(data){
return data;
}
$.get('/api/'+ID+'/info', function(data) {
return callback(data);
});
}
Related
I am currently learning javascript and i find this often on tuitorials i watch. Javascript automatically pass the result of a function as a parameter to the next function.
fetch('SampleApi')
.then( function(data) { return data.json() } )
.then( function(data) { console.log(data.name); } )
.catch(function(err) { console.log(error) })
the result of the fetch goes directly as a parameter to the function then the result of the function goes directly as a parameter to the next function.
I just want to know what is this and what is this called. I need to understand this before diving deep.
EDIT: modified example to show passing parameters between methods to directly answer the qeustion.
The underlying premise is that JavaScript can act upon a returned result immediately as long as the operation requested is sane and available. A simple example is shown here:
console.log("|" + " This is a string with extra spaces ".trim() + "|");
This can be accomplished in your own code as well. In the following example, note the return of the operation is the object itself. This allows us to carry out any operation available within that object upon the return from some other operation in the same object:
class Test {
sayHi(message = "Passed Message") {
console.log('parameter: ' + message);
this.message = message;
return this;
}
sayBye(cb) {
cb(this.message);
return this;
}
}
const test = new Test();
test.sayHi("hello!").sayBye(passed => console.log('passed in: ' + passed));
So throughout your experience with JavaScript you will find many instances of this idea that is called method chaining.
In the example you provided, it is simply this concept applied to the fetch API.
It's called function chaining.
The fetch function returns an object that has a function then, which accepts a function as its parameter.
When then is called, it executes the given function with the data stored within the object as a parameter, and stores the return value as the new internal data, which is passed to the function in the second then call. then returns the same object that was returned from fetch.
The call to catch will check if an error was encountered (and stored into the object), and if so, will call the given function with the error.
You could spread out the whole thing, without chaining:
var o = fetch('SampleApi');
o = o.then(function(data) { return data.json(); });
o = o.then(function(data) { console.log(data.name); });
o.catch(function(error) { console.log(error); });
The short answer is "That's just how promises work"
To better illustrate WHY this is good behavior:
function getOrganizedData() {
return fetch("somedata").then(function(rawData) {
// do some stuff
return organizedData;
});
}
Anyone using this function will be able to use the organizedData - so if you have a shared service, only that service needs to know how to translate rawData into organizedData for the rest of the application to use.
What if that's not the behavior you want?
Easy, just don't chain the promises:
const fetchPromise = fetch('SampleApi');
fetchPromise.then( function(data) { return data.json() } );
fetchPromise.then( function(data) { console.log(data.name); } );
fetchPromise.catch(function(err) { console.log(error) });
But wait, won't I get errors with that approach because the catch isn't applied to each chain?
Yes! So, you need to catch BOTH chains:
const errorFunction = function(err) { console.log(error) };
const fetchPromise = fetch('SampleApi');
fetchPromise.then( function(data) { return data.json() } ).catch(errorFunction);
fetchPromise.then( function(data) { console.log(data.name); } ).catch(errorFunction);
NOTE: This chaining behavior applies to .catch as well:
fetch("somethingThatWillError")
.then(function() { console.log("THEN - I will not run because there's an error"); })
.catch(function() { console.log("CATCH - I will run because I am handling the error"); })
.then(function() { console.log("THEN - I will run because the error has been handled"); })
.catch(function() { console.log("CATCH - I will not run because there is no error to handle"); })
Edit : For resume, the result of fetch is the first data in case of success, the second data is the return of the first then and err the result of fetch in case of reject.
You are actually playing with promise here, you can look some documentation about Promise here.
In javascript your running task can be synchronous or asynchronous, promise is for handling asynchronous task. You can know more about asynchronous here.
To be quick and simple a promise can be resolve now, in ten minutes or never. When you are building a Promise, you need an executor which is passed with two arguments, a resolve function and a reject function, they will be call just before the final resolution of your promise, (after an API call for exemple).
The reject gonna be call if your promise fail (timed out on your api call for example) and the resolve gonna be call on the success of your promise. In this two callbacks (resolve and promise), if you don't know about callback you have to learn it here, you will have an argument with the return of the resolution of your promise, for example if the promise is rejected you gonna have the reason in a message and in the resolve you gonna have the data that you need from your api call.
In your case, the fetch is build like fetch = new Promise(...); where then(...) is the success resolver and catch(...) the rejected resolver. You can have a finally(...) which is called after the then() and catch().
And the data in your example is the result of your promise if it's a success and err is the result in case of error result.
I have a file where I code my whole connection with the REST service, and it works.
From another file, I am executing the following lines (everything works)
this.baseService.getCars(ID)
.subscribe(cars=> this.cars= cars);
To access to the values of the response I was using HTML. For example: *ngIf="cars"
Now, I would like to access by Javascript to the variable doing this:
this.baseService.getCars(ID)
.subscribe(cars=> this.cars= cars);
console.log(this.cars)
but I get undefined but I can access by HTML. I know that it is a stu**d question, but how should I do it? Which variable does contain the variable?
The execution order of those lines of code is not what you think it is.
To see cars in console, change your function to this:
this.baseService.getCars(ID)
.subscribe(cars=>{
this.cars= cars;
console.log(this.cars);
});
You need to place the console.log inside subscribe
this.baseService.getCars(ID)
.subscribe(
cars=> {
this.cars= cars;
console.log(this.cars);
},
error => {
console.log(error);
}
);
Subscribe is asynchronous, like a Promise, but isn't a Promise so, when you execute the code, the subscribe is fired, then the console log. But When the console.log is executing, subscribe is running yet, so that's why you get undefined.
You can do the console.log inside the callback function in subscribe
this.baseService
.getCars(ID)
.subscribe(cars=> {
this.cars = cars
console.log(this.cars)
});
Another solution is to use async/await. You can't use async/await directly with subscribe, because IT'S NOT A PROMISE. Fortunately Observers can be converted to a Promise.
So, in you service you can return a promise, like this:
getCars() {
// your service stuff
return this.api.get(url).toPromise().then( res => res.data); // This is the important part.
}
Then, in your component, call it with async/await:
async yourFunction() {
this.cars = await this.baseService.getCars(ID);
console.log(this.cars);
}
Now you can log this.cars after the getCars()
Hope this helps you.
I basically want to have a function which is GET'ing a page and then returns a result based on that, this leads to the following problem:
All GET methods are using a callback function, meaning that the function will end before the result is there. How do I pause the thread until the callback is fired and then return the result of the callback in my main function?
function someFunction(){
$.get(
"blabla.php",
function(data) {
// This data is supposed to be returned by "someFunction"
}
)
return //the data we retrieved from 'blabla.php'
}
How would you accomplish this?
EDIT: I know this concept from lua, however I'm not entirely sure if someone would call it "Yielding a function result". Please correct me, if I'm wrong.
Correct answer: don't. Asynchronous is the correct way to do HTTP requests. However, there are cases when you want to block while waiting on a result, so in those cases:
Use the async parameter.
function somefunction() {
var retVal;
$.ajax({
url: "blabla.php",
success: function(data) {
// modify data here
retVal = data;
},
async: false
});
return retVal;
}
Instead of returning something from your function why not just take a callback as an argument?
function someFunction(callback) {
$.get("blabla.php", function (data) {
callback(data);
});
// Or simply $.get("blabla.php", callback) if the data need not be modified before calling the callback.
}
Which can then be used like so:
someFunction(function (data) {
alert("Data received!");
});
JavaScript is single-threaded, so this is really the only reasonable way to accomplish something like this without blocking the entire script.
I've this function
function getTags(level){
$.getJSON("php/get-tags.php", { "parent": level }, function(json) {
return json;
});
}
I'm calling this function as
$(function(){
var tags = getTags('0');
});
The problems is, in the function getTags() the return json is like
{"tags":["Mathematics","Science","Arts","Engineering","Law","Design"]}
but at var tags = getTags('0'), catching the return value, it gets an undefined value.
Is the way I'm returning the value incorrect?
Like many others already correctly described, the ajax request runs asynchronous by default. So you need to deal with it in a proper way. What you can do, is to return the jXHR object which is also composed with jQuery promise maker. That could looke like
function getTags(level){
return $.getJSON("php/get-tags.php", { "parent": level });
}
and then deal with it like
$(function(){
getTags('0').done(function(json) {
// do something with json
});
});
getJSON is asynchronous, the function that called it will have finished by the time the HTTP response gets back and triggers the callback.
You cannot return from it.
Use the callback to do whatever you want to do with the data.
You are trying to call an asynchronous function in a synchronous fashion.
When you call getTags, the it triggers a call to your PHP page, if javascript was blocking, then your page would hang until your server responded with the JSON. You need to re-think your logic and trigger a callback.
function getTags(level){
$.getJSON("php/get-tags.php", { "parent": level }, function(json) {
//do something with the JSON here.
});
}
You cannot return from an AJAX call. It's asynchronous. You need to have all logic dealing with the returned data in the callback.
If you need it to work like that, your getTagsfunction must return a value. It does not at the moment. You could achieve this by using $.ajax and setting async to false.
I have a problem returning a variable in my function, the below script works fine:
function sessionStatus(){
$(document).ready(function(){
$.getJSON(scriptRoot+"sessionStatus.php",function(status){
alert(status);
});
});
}
sessionStatus();
Bet when I try the following I get a message box with the message "undefined":
function sessionStatus(){
$(document).ready(function(){
$.getJSON(scriptRoot+"sessionStatus.php",function(status){
return status;
});
});
}
alert(sessionStatus());
This is really bugging me, I just can't seem to see what I've done wrong.
There are two things you should know:
1: the JSON thing is asynchronous, so the function call to sessionStatus could already be done when the JSON is still being fetched. The following would work:
function sessionStatus(callback){
$(document).ready(function(){
$.getJSON(scriptRoot + "sessionStatus.php", function(status){
callback(status);
});
});
}
sessionStatus(function(s){alert(s);});
or rather:
function sessionStatus(callback){
$(document).ready(function(){
$.getJSON(scriptRoot + "sessionStatus.php", callback);
});
}
sessionStatus(function(s){alert(s);});
2: even when it would be synchronous, you are only giving a return value from the inner function, so the sessionStatus would return nothing. Check out this code (not related to your JSON thing):
function do() {
var x = 0;
(function(){
x = 2;
})();
return x;
}
or:
function do() {
var x = (function(){
return 2;
})();
return x;
}
Both return 2. Hopefully this explains a bit.
Your function sessionStatus() doesn't return anything, hence the alert says undefined.
All the function does is set thing up for the AJAX call to happen once the page loads - nothing actually happens within sessionStatus() that could be returned.
The code in function(status) { ...} doesn't get run until the AJAX call to the server returns a value, and that AJAX call doesn't get sent until the page loads.
You ought to read up on what $.getJSON() and $(document).ready() actually do, because you're going to continue to get confusing behaviour until you understand them properly.
Your sessionStatus() function never returns anything. It sets a function to run later, and that function returns something, but that's not anything to do with sessionStatus()
You're returning a value when the document ready event is done. Where is your value supposed to go? The jQuery object doesn't know what to do with it.
The function sessionStatus just sets up the event listener for $(document).ready() and then returns without returning a value. That's the undefined you see.
Later when $(document).ready() fires it calls the ajax which if it succeeds returns the status, but nothing is receiving that status.
function sessionStatusCallback(status)
{
alert(status);
}
function sessionStatus(){
$(document).ready(function(){
$.getJSON(scriptRoot+"sessionStatus.php",function(status){
sessionStatusCallback(status);
});
});
}
sessionStatus();
Your function is being called asynchronously -- actually after two asynchronous calls, made via .ready() and .getJSON(). In such a case there is no available return value, instead you have to use a callback, as in the above, to process the response.
Though I should note that the function passed to getJSON in the above already is a callback. You could change that function definition to just be "sessionStatusCallback" and it would call the above callback once the JSON was ready, and you could continue to process there. Or...continue your processing in the current callback (it's a matter of style whether to use a function reference or declare the anonymous function right there in the .getJSON() call)
Functions should never be included in a jQuery(document).ready function. Separate them, so you don´t have side effects you don´t want to have. How do you want to call the session status? And witch function should get the return value?