I have some JavaScript code which pulls data out of 2 sources.
1st source is local web storage
2nd source is AJAX request.
The condition is simple :
function getMyData(){
if (window.localStorage['myData'] != null)
{
return window.localStorage['myData'];
}
else
{
networkTools.ajax("geyMyData", function (data)
{
return data;
})
}}
But the problem is that AJAX is async process and I don't want the code to continue until getMyData() return something.
I know that I can use callbacks, but would like just to wait until this function returns something and then continue the execution. (Not only for this case, but for general knowledge.)
Is that possible?
Use callback
function getMyData(callback){
if (window.localStorage['myData'] != null)
{
callback(window.localStorage['myData'];)
}
else
{
networkTools.ajax("geyMyData", function (data)
{
callback(data);
})
}
}
How to call getMyData
getMyData(function(data){
//your code
});
Or you can make your ajax request as a synchronous request.
The best way to do this is with a callback function.
You don't want your JavaScript to block for things like network requests because your web page would become completely unresponsive.
This is how you would do it:
function getMyData(callback){
if (window.localStorage['myData'] != null)
{
return window.localStorage['myData'];
}
else
{
networkTools.ajax("geyMyData", function (data)
{
callback(data);
});
}
}
The best option would be to call getMyData with a callback and break your functions up into caller -> getMyData -> callback. it is possible to do a synchronous request but you'd better not because it locks up the browser until the call is done.
function gotData(data){
console.log("got data:",data);
}
function someFunction(){
...
getMyData(gotData);
}
function getMyData(callback){
if (window.localStorage['myData'] != null)
{
callback(window.localStorage['myData']);
}
else
{
networkTools.ajax("geyMyData", function (data)
{
callback(data);
})
}}
I did not include how to do a synchronous request because it's bad to do so but if you really want to do it than how to it can be found anywhere (like MDN developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest)
Related
Within my code I have a function that depends on the result of an async call to an API endpoint. In order for the function to execute properly it needs to wait for the result of the call. So I've read up on async calls and from another Stack Overflow question I read that you should make use of callback functions to enable correct execution.
The code below is my attempt to make use of callbacks to allow my function to run successfully, but it doesn't work and at the moment I think the calls are messed up.
I'm not sure how I need to structure this code, but I first need the getInstructionType() call to return its value, then the GetValidationResult() call to return its value, and then the setValidationRowColor() function needs to execute.
getInstructionType(applicationNumber, function(result) {
getInstructionValidationResult(applicationNumber, function(type) {
setValidationRowColor(result);
});
});
function getInstructionValidationResult(applicationNumber) {
var url = //-snip-;
$.get(url, function(data) {
return data;
});
}
function getInstructionType(applicationNumber) {
var url = //-snip-;
$.get(url, function(data) {
return data;
});
}
You could add arguments to the functions which you can use as callbacks. Then you can call those when the AJAX request completes, something like this:
getInstructionType(applicationNumber, function(result) {
getInstructionValidationResult(applicationNumber, function(type) {
setValidationRowColor(result);
});
});
function getInstructionValidationResult(applicationNumber, callback) {
$.get(/*-snip-*/, function(data) {
// some custom logic to work with the response here...
callback && callback(data);
});
}
function getInstructionType(applicationNumber, callback) {
$.get(/*-snip-*/, function(data) {
// some custom logic to work with the response here...
callback && callback(data);
});
}
The alternative to callbacks (which are completely valid) are promises - which is really just another form or callbacks. Assuming you are using jQuery's $.get, you are already making use of Promises:
getInstructionType(applicationNumber, function(result) {
return getInstructionValidationResult(applicationNumber)
.then(function() {
setValidationRowColor(result)
})
});
function getInstructionValidationResult(applicationNumber) {
var url = //-snip-;
return $.get(url)
}
function getInstructionType(applicationNumber) {
var url = //-snip-;
return $.get(url)
}
Note that all I did was return the $.get and added a .then which accepts your callback inside getInstructionType
I'm probably missing the point somewhere here so I'm looking for advice.
I have a nodejs server which is listening for client connections and, based on the data received, makes calls to an API.
The very first call to that API gets an ID which needs to be used on subsequent calls to group them together.
Where I'm struggling is that the call to the API is necessarily asynchronous and in the callback I'm assigning the ID to a variable. While that async call is being processed by the API server, more data is coming in from the client and needs more API calls made BUT I can't fire them until I know the results from the first call as the second calls depend on it.
What's the proper way to handle this? I feel like I should be using Q to promise the results of the first API call to the second, but I'm not sure how it should be structured. Or should I just be queueing up the API calls until the first completes? How would I do that?
Example problem code :
var server = net.createServer();
//set up the callback handler
server.on('connection', handleConnection);
handleConnection(conn) {
//do some stuff...
firstAPICall();
conn.on('data', handleData);
}
handleData(data) {
//do some stuff...
otherAPIcall();
}
firstAPICall() {
client.get("http://myAPI/getID", function (data, response) {
conn.myID = data[0].myID;
}
}
}
otherAPICall() {
//How do I make sure I actually have a value
//in conn.myID from the first function???
client.post("http://myAPI/storeData", { data: {myID:conn.myID, data:someData} }, function (data, response) {
//do some stuff...
}
}
}
Yes, you should be using promises for this. Make a promise for the id that is asynchronously resolved from the first call, and then use it in the subsequent calls:
handleConnection(conn) {
//do some stuff...
var idPromise = firstAPICall();
conn.on('data', function handleData(data) {
//do some stuff...
otherAPIcall(idPromise).then(function(result) {
…
});
});
}
firstAPICall() {
return Q.Promise(function(resolve, reject) {
client.get("http://myAPI/getID", function (data, response) {
resolve(data[0].myID);
});
});
}
otherAPICall(idPromise) {
return idPromise.then(function(myID) {
return new Promise(function(resolve, reject) {
client.post("http://myAPI/storeData", {
data: {myID:myID, data:someData}
}, function (data, response) {
//do some stuff...
resolve(…);
});
});
});
}
Probably you should factor out creating a promise for the result of a client.get call in an extra function. Also make sure to handle errors correctly there and call reject with them. If client would use the node callback conventions, Q even has some nice helper functions for that.
Try using promises. Then use 'then' to call the otherAPICall()
I think you can assume they will be sending data immediately after connecting. So you can simplify and just check in otherAPICall if you have an ID, if not, you can just use a callback. Promises or the async/await keywords might make things sort of nicer down the line but aren't required for this.
var server = net.createServer();
//set up the callback handler
server.on('connection', handleConnection);
handleConnection(conn) {
conn.on('data', handleData(connm, data));
}
handleData(conn, data) {
//do some stuff...
otherAPIcall(conn);
}
checkID(conn, cb) {
if (!conn.myID) {
client.get("http://myAPI/getID", function (data, response) {
conn.myID = data[0].myID;
cb();
});
} else {
cb();
}
}
otherAPICall(conn) {
checkID(conn, function() {
client.post("http://myAPI/storeData", { data: {myID:conn.myID, data:someData} }, function (data, response) {
//do some stuff...
});
});
}
promises can chain values and are always resolved after the callback occurs with the returned value,
function async(value) {
var deferred = $q.defer();
var asyncCalculation = value / 2;
deferred.resolve(asyncCalculation);
return deferred.promise;
}
var promise = async(8)
.then(function(x) {
return x+1;
})
.then(function(x) {
return x*2;
})
.then(function(x) {
return x-1;
});
promise.then(function(x) {
console.log(x);
});
This value passes through all the success callbacks and so the value 9 is logged ((8 / 2 + 1) * 2 - 1).
I have an array of url and the requirement is that I have to make http.get requests in a synchronous manner. Only after the first url call is successful, the second one should be called
for(var i in urlArray)
{
/*do some operations and get the next url*/
$scope.alpha(newURL);
}
$scope.alpha = function (newURL) {
$http.get(newURL) // these calls should be synchronous
.success(function () {
})
.error(function () {
});
}
How do I do this?
It seems what you really want to is make the calls sequentially, not necessarily synchronously.
In that case, don't use a loop (because it's synchronous). Simply make the next call in response to the previous call.
Simplified example:
var i = 0;
makeRequest(urlArray[i], function success() {
var nextURL = urlArray[++i];
if (nextURL) {
makeRequest(nextURL, success);
}
});
where makeRequest is the function that makes the Ajax request and calls the callback on success:
function makeRequest(url, callback) {
$http.get(url).success(callback);
}
I am assuming that you want call them sequentially, in that case, you can use something like a recursion, call the function inside the .success callback
var currentURL; // calculate teh currentURL
$scope.alpha(currentURL);
$scope.alpha = function (newURL) {
$http.get(newURL) // these calls should be synchronous
.success(function (response, status, headers, config) {
//get the response
//generate the new currentURL as per your need
//keep a break condition, to exit
$scope.alpha(currentURL);
})
.error(function () {
});
}
2) alternately you can you $q, deferred calls to achieve this
Hope this helps
I'm using the facebook js sdk, I'm trying to get a list of all groups, the response is pageinated. So recursion seems like a an obvious solution for this
function handlePaging(response) {
if (response && !response.error) {
if (response.paging.next) {
return response.data.concat(FB.api(response.paging.next, handlePaging));
} else {
return response.data;
}
}
}
console.log(FB.api("/me/groups", handlePaging));
But because its asynchronous I'm getting undefined returned. I've had a look at other returning values from asynchronous requests, but none of them were recursive, and the answer was use a callback, which I have.
I'm not even sure if this is even possible.
The call to FB.api is asynchronous so it returns immediately usually before the call to the server is made and the handlePaging callback is invoked. Try something like this
var data = [];
function handlePaging(response) {
if (response && !response.error) {
data = data.concat(response.data);
if (response.paging.next) {
FB.api(response.paging.next, handlePaging);
} else {
console.log(data);
}
}
}
FB.api("/me/groups", handlePaging);
So I'm doing a an ajax call in this function somewhat like this:
function getCount() {
$.get("/People/getCount", function (data) {
if (data && data != "") {
// lots of code in here
}
What I'm doing in another function is making a second call like this:
function worldPeople() {
return $.get("/People/getCount", function (data) {
if (data != 0) {
var target = $("#worldNumbers").find("span");
target.html(data.length).digits();
}
})
}
So I really would like to avoid making that second call. Is there any good way in avoiding that? Maybe do some chaining or such, reusing the callback from the first one? I've heard that its bad practice to do several calls.
Regards
Would like to thank all who answered. In the end did not use any of the solutions, I solved it in another way. I'm sure most of the examples you gave me were really good. Do not know how to do with accepting answers. Accept all or none?! Thanks!
You could create a simple data store:
App.store = function () {
this.people = null;
this.count
loadPeople = function () {
if(this.people === null) {
$.get("/People/getCount", function (data) {
if (data != 0) {
this.count = (data.length).digits();
this.people = data;
}
}
};
}
What about store count of peoples in hidden field? And than check this field before sending request.
You can achieve this by handling your Ajax requests using some sort of cache. I use a cache that saves the information retrieved based on the url it called. If another function sets off the same request the cache returns the alraedy fetched data.
What you do need to do as well though is check if the data is outdated so you can refetch it if necessary.
Well, you can just send the function pointer to the function that executes $.get
basically you would then do this:
function worldPeople() {
getCountFromServer(function(data){
//do sth with data
});
}
function getCount() {
getCountFromServer(function(data){
//do sth with data
});
}
function getCountFromServer(callback) {
return $.get("/People/getCount", function (data) {
if (data)
callback(data);
});
}
I generally use a caching module pattern for this kind of thing:
// create a quick singleton to store cached data
var People = (function() {
// private variable to act as cache
var count;
// function to get cached data
// note: You have to assume it's always asynchronous
function getCount(callback) {
// have we loaded the data yet?
if (count===undefined) {
// cache miss: load the data, store it, do the callback
$.get("/People/getCount", function (data) {
count = data;
callback(data);
}
} else {
// cache hit - no need to reload
callback(count);
}
}
// provide access to the getter function
return {
getCount: getCount
};
}());
The first time you hit the cache, it'll load from the server; the second time it will load from the private variable.
// will load the data asynchronously
People.getCount(function(count) {
alert("First hit: " + count);
});
// will use the cached data
People.getCount(function(count) {
alert("Second hit: " + count);
});
Depending on the complexity you want to support, you could add additional features like expiring the cache after a particular interval, caching multiple calls (potentially keyed to the AJAX URL), etc. I like to keep the API simple and not reference the AJAX URLs - that way your cache acts like an abstracted service layer, and you can create other cache implementation to work with different data sources - useful for things like stubbing out data before you've implemented your server-side AJAX handlers.