I have an asynchronous Ajax function which runs a command string at the server side and returns the result to the client. It calls a callback to process the result.
function ajaxCall(commandStr,callback){
var url=......//make a url with the command string
jquery.get(url,function(result){
//process the result using callback
callback(result);
});
}
The asynchronous call (ajaxCall) may take a while to be finished but I want it to do the same command after an interval (1000ms).
I want to write a function that is like this:
function ajaxCallRepeated(interval,commandStr,callback)
I tried closures like this:
function ajaxCallRepeated(interval,commandStr,callback){
//This feature uses closures in Javascript. Please read this to know why and how: http://jibbering.com/faq/notes/closures/#clSto
function callLater(param1,param2,param3){
return (function(){
ajaxCall(param2,function(out,err){
if(param3)param3(out,err);
var functRef = callLater(param1,param2,param3);
setTimeout(functRef, interval);
});
});
}
//the first call
var functRef = callLater(interval,commandStr,callback);
setTimeout(functRef, interval);
}
Then I call it like this:
ajaxCallRepeated(2000,"ls",function(result){
alert(result);
});
But it only runs the command 2 times.
How can I write a function that will reschedule itself after it is called as a callback of an asynchronous function?
PS. I want to fire another Ajax call after the previous one is finished. Also, it worth to mention that axashCallRepeated() will be called with various parameters, so several Ajax calls are running in parallel, but for each commandStr, there is only one Ajax call going on, and after the Ajax call returns, another one will be fired after X seconds.
I would not use setTimeout to trigger the second Ajax call ! Because you never know how long it will take and if it's finished !
As far as you tagged your question right and you ARE using jquery you should consider something like this:
$.ajax({
type: 'POST',
url: url,
data: data,
success: function(){
// The AJAX is successfully done, now you trigger your custom event:
$(document).trigger('myAjaxHasCompleted');
},
dataType: dataType
});
$(function(){
//somehwere in your document ready block
$(document).on("myAjaxHasCompleted",function(){
$.ajax({
//execute the second one
});
});
});
So this would ensure that the ajax post is DONE and was successful and now you could execute the second one. I know its not the exact answer to your question but you should consider on using something like this ! Would make it safer I guess :-)
The key to solve this problem is to save a reference to the closure itself and use it when scheduling the next call:
function ajaxCallRepeated(interval,commandStr,callback){
//This feature uses closures in Javascript. Please read this to know why and how: http://jibbering.com/faq/notes/closures/#clSto
function callLater(_interval,_commandString,_callback){
var closure=(function(){
ajaxCall(_commandString,function(out,err){
if(_callback)_callback(out,err);
setTimeout(closure,_interval);
});
});
return closure;
}
//now make a closure for every call to this function
var functRef = callLater(interval,commandString,callback);
//the first call
functRef();
}
It becomes easier to reason about if you separate things up a bit.
For example, the repetition logic doesn't have to know about AJAX or callbacks at all:
function mkRepeater(interval, fn, fnScope, fnArgs) {
var running;
function repeat() {
if (!running) return;
fn.apply(fnScope, fnArgs);
setTimeout(repeat, interval);
}
return {
start: function() { running = true; repeat(); },
stop: function() { running = false; }
};
}
You can use it like this:
var r = mkRepeater(2000, ajaxFunction, this, ["getStuff", callbackFn]);
r.start();
...
r.stop();
Related
I am writing some javascript that includes a series AJAX calls and I am looking for an elegant solution to the following issue: The goal of the script is to gather parameters and then execute an API call with these parameters. The very first time the call is executed there is one parameter that needs to be requested from the server - every subsequent call will use a stored value of this parameter. This is where the issue begins. I want a conditional AJAX call to be made only if this is the first time. I don't want to put the rest of the code into the success function of that AJAX call as that seems convoluted. I would like something like the following but due to the obvious asynchronous nature of the call I realize this is not possible. I also want to avoid having a synchronous call as this would cause the thread to block:
var myParameter;
if(!params.myParam.isStored) {
myParameter = getParamWithAjaxCall();
} else {
myParameter = params.myParam;
}
// Continue with the rest of execution here of which there is a lot of code
Sorry if this seems like an obvious question and I have looked into solutions using the following but I am looking for an experienced opinion on what the most elegant solution would be:
jQuery: when.done
jQuery: async: false
Passing a callback to the Ajax call
I would create a wrapper function which you pass your logic to as a callback in done(). Something like this:
function makeRequest(callback) {
if (!params.myParam) {
// retrieve param
$.ajax({
url: '/getParam',
success: function(data) {
params.myParam = data.param;
}
}).done(callback);
}
else {
// param already has a value...
callback();
}
}
makeRequest(function() {
// make your AJAX request here, knowing that params.myParam will have a value.
});
You could use promises like so (I have used JQuery promises here):
function ParameterValueProvider() {
var parameterValue;
return function() {
var deferred = $.Deferred();
if ( parameterValue === undefined ) {
$.ajax({
// ... ajax parameters go here
}).done(function(rsp) {
parameterValue = rsp;
deferred.resolve(parameterValue);
});
}
deferred.resolve(parameterValue);
return deferred;
}
}
// Your Application
(function() {
'use strict';
var getParam = ParameterValueProvider();
// this will get the value from server the firs time
// and subsequent calls will use the cached value
getParam().then(function() {
// subsequent ajax calls go here
});
}());
I have two javascript functions
searchButtonClicked()
hidemap()
The searchButtonClicked() will be called in body onload.
The searchButtonClicked() sometimes not respond. So I want to let it try to execute for 5 seconds and if it dosent execute I want to call hidemap().
Please help. Thanks in advance.
Udy
here is the function
function searchButtonClicked() {
if (inlocatorHubMode) {
locatorHubSearch();
}
else {
search();
}
}
I'm assuming searchButtonClicked is an AJAX request.
If this is the case, you can achieve what you're after using a combination of setTimeout and a callback.
var searchTimeout = null;
function searchButtonClicked(){
searchTimeout = setTimeout(requestFailed, 5000);
//If you're using jQuery, something like this
$.ajax({
url: "service.php",
success: requestSuccess
});
//If you're not, something like this
xmlhttp.onreadystatechange=(function(){
if(xmlhttp.readyState==4 && xmlhttp.status==200){
requestSuccess();
}
});
}
function requestSuccess(){
clearTimeout(searchTimeout);
//Yay! It succeeded!
}
function requestFailed(){
hideMap();
//It didn't unfortunately =[
}
Edit: An edit was made to say that it's not in fact an AJAX request. Leaving this here in case it's useful to somebody.
There is a function in javascript
window.setTimeout("javascript function",milliseconds);
This functions waits the specified number of milliseconds, and then executes the specified function.
In your case you can set timeout at the beginning of your function.
So that after the specified milliseconds, your second function will execute
I have one function using to get data from text file and alert it in another function.
var library_name; // Global Variable
function getLibraryName(){
jQuery.get('stream.txt', function(data) {
library_name = data.toString().substring(17,data.length);
});
}
function saveFeedback() {
alert(library_name);
}
When saveFeedback is called, it will alert library_name
I have been tried to put it in the same function like this
function saveFeedback() {
jQuery.get('stream.txt', function(data) {
library_name = data.toString().substring(17,data.length);
});
alert(library_name);
}
but it is still keep saying undefined in console
How to solve this out? Without using parameter because saveFeedback function has to be called from somewhere else.
The second parameter to .get is called when the get finishes. You're saying, "fetch stream.txt and when it finishes, execute this function". The following calls saveFeedback when the get is finished:
function getLibraryName(){
jQuery.get('stream.txt', function(data) {
library_name = data.toString().substring(17,data.length);
saveFeedback();
});
}
Because get is asynchronous, your interpreter will instantiate the saveFeedback function before the success function is called (although some sanity checkers like JSLint probably want you to define saveFeedback before getLibraryName.
You shoud use saveFeedback() function in $.get(data) callback, because it it async request and you do not know, when it will be completed.
You can make a hidden input field and attach data to it using $.data();
For example:
function getLibraryName(){
jQuery.get('stream.txt', function(data) {
var library_name = data.toString().substring(17,data.length);
$('#myHiddenField').data('library_name', library_name);
});
}
function saveFeedback() {
alert($('#myHiddenField').data('library_name'));
}
jQuery get initiates an asynchronous call. Asynchronous means that the result from it will return later... maybe a short time later, maybe many seconds later. After kicking off this call, the code then proceeds immediately to the next line which is your alert. This alert occurs before the return from the asynchronous call, so of course the variable is still undefined--it hasn't been set yet.
One way to solve the problem might be to tell jQuery to make the call synchronous. But this is the wrong answer because it will stop the browser from responding to user interaction while the call is occurring. Instead, you need to adjust how you think about asynchronous calls. The basic concept is that instead of having a big function that does things step by step like a normal, procedural recipe (do step 1, then step 2, then step 3), instead you have to set up two different recipes or pieces of code: one to initiate the ajax call and then exit, and one to respond to the result of the ajax call.
Now, you already have two functions. You can't combine them for the reasons I already explained. But, you can chain them, or encapsulate one inside the other:
var library_name;
function saveFeedback() {
alert(library_name);
}
function getLibraryName(){
jQuery.get('stream.txt', function(data) {
library_name = data.toString().substring(17 ,data.length);
saveFeedback(); // this chains the two functions together
});
}
or maybe even better:
var library_name;
function saveFeedback() {
alert(library_name);
}
function receiveLibraryName(data) {
library_name = data.toString().substring(17, data.length);
saveFeedback();
// additional statements here after saving
}
function getLibraryName(){
jQuery.get('stream.txt', receiveLibraryName);
}
The point is that you cannot continue in the next statement as usual within the getLibraryName function. The steps you wish to take after the ajax call must be inside of another function: either in the callback function itself, or in another function called from the callback function.
Try following code:
var library_name;
function getLibraryName(){
jQuery.get('stream.txt', function(data) {
let lbName= data.toString().substring(17,data.length);
saveFeedback(lbName);
});
}
function saveFeedback(lbName) {
library_name = lbName; //If you are using library_name in other code
alert(library_name);
}
You have to define library_name before jquery.get once. Because if the jquery.get doesnt work library_name will not be created by the time u call alert.
please add
var library_name;
as the first line of saveFeedback().
If I use a closure to define something is there a means of waiting so to speak until the variable is populated before moving on to the next bit.
Example:
var myVari = someFunction();
$.each(myVari, function(){/*code for each*/});
the function that defines myVari is an AJAX call, which can take a second or 4 (yea its not to fast) to define the variable. Problem is, before the AJAX call yields its results the $.each has already fired off and errored due to myVari being empty. Is there a better way to approach this scenario?
You should adapt your code so that you can pass a callback to someFunction, which you execute when the AJAX call is completed.
The only way you can wait for the AJAX call to complete is to change the call to synchronous, but this is heavily discouraged as it locks up the browser completely for the duration of the AJAX call.
Because you are already using the jQuery libary, this process of callbacks becomes a whole lot easier. Instead of returning the variable like you are at the moment, I'd return the jQuery AJAX object (which has a promise interface as of 1.6), so you can easily add callbacks to it:
function someFunction () {
return jQuery.ajax('some/url.php', {
// whatever
});
}
var myVari = someFunction();
myVari.done(function (data) {
$.each(data, function(){/*code for each*/});
});
If I understand what you are trying to do, then you could try your $.each inside the 'success' handler of your ajax call.
Rewrite someFunction to something like -
var myVari; //define this here or in whichever calling scope where it needs to be available.
$.ajax({
'url': 'http://..',
'type': 'GET', // or POST
'data': { } // whatever data you need to send
'success': function(data) {
myVari = process_the_server(data);
$.each(myVari, function() {...});
}
});
Use a callback, like this:
someFunction(function(myVari) {
$.each(myVari, function(){ /*code for each*/ });
});
Then redefine someFunction like this:
function someFunction(callback) {
var myVari;
/* ... */
/* calcuate myVari */
/* ... */
/* instead of returning it, pass it to the callback: */
callback(myVari);
}
The correct way is: Instead of running the each on its own, run it inside the ajax call.
You could, I suppose do:
function checkFunc() {
setTimeout(function() {
if(myVari) {
$.each(........);
} else {
checkFunc();
}
}, 1000);
}
That not really good coding practice, but it will work.
If I have an ajax call off fetching (with a callback) and then some other code running in the meantime. How can I have a third function that will be called when both of the first 2 are done. I'm sure it is easy with polling (setTimeout and then check some variables) but I'd rather a callback.
Is it possible?
You could just give the same callback to both your AJAX call and your other code running in the meantime, use a variable to track their combined progress, then link them to a callback like below:
// Each time you start a call, increment this by one
var counter = 0;
var callback = function() {
counter--;
if (counter == 0) {
// Execute code you wanted to do once both threads are finished.
}
}
Daniel's solution is the proper one. I took it and added some extra code so you don't have to think too much ;)
function createNotifier() {
var counter = 2;
return function() {
if (--counter == 0) {
// do stuff
}
};
}
var notify = createNotifier();
var later = function() {
var done = false;
// do stuff and set done to true if you're done
if (done) {
notify();
}
};
function doAjaxCall(notify) {
var ajaxCallback = function() {
// Respond to the AJAX callback here
// Notify that the Ajax callback is done
notify();
};
// Here you perform the AJAX call action
}
setInterval(later, 200);
doAjaxCall(notify);
The best approach to this is to take advantage of the fact that functions are first-order objects in JavaScript. Therefore you can assign them to variables and invoke them through the variable, changing the function that the variable refers to as needed.
For example:
function firstCallback() {
// the first thing has happened
// so when the next thing happens, we want to do stuff
callback = secondCallback;
}
function secondCallback() {
// do stuff now both things have happened
}
var callback = firstCallback;
If both your pieces of code now use the variable to call the function:
callback();
then whichever one executes first will call the firstCallback, which changes the variable to point to the secondCallback, and so that will be called by whichever executes second.
However your phrasing of the question implies that this may all be unnecessary, as it sounds like you are making an Ajax request and then continuing processing. As JavaScript interpreters are single-threaded, the Ajax callback will never be executed until the main body of code that made the request has finished executing anyway, even if that is long after the response has been received.
In case that isn't your situation, I've created a working example on my site; view the source to see the code (just before the </body> tag). It makes a request which is delayed by the server for a couple of seconds, then a request which receives an immediate response. The second request's response is handled by one function, and the first request's response is later handled by a different function, as the request that received a response first has changed the callback variable to refer to the second function.
You are talking about a thing called deferred in javascript as #Chris Conway mentioned above. Similarly jQuery also has Deferred since v1.5.
Check these Deferred.when() or deferred.done()
Don't forget to check jQuery doc.
But to give you some idea here is what I am copying from that site.
$.when($.ajax("/page1.php"), $.ajax("/page2.php")).done(function(a1, a2){
/* a1 and a2 are arguments resolved for the
page1 and page2 ajax requests, respectively */
var jqXHR = a1[2]; /* arguments are [ "success", statusText, jqXHR ] */
if ( /Whip It/.test(jqXHR.responseText) ) {
alert("First page has 'Whip It' somewhere.");
}
});
//Using deferred.then()
$.when($.ajax("/page1.php"), $.ajax("/page2.php"))
.then(myFunc, myFailure);
Something like this (schematic):
registerThread() {
counter++;
}
unregisterThread() {
if (--counter == 0) fireEvent('some_user_event');
}
eventHandler_for_some_user_event() {
do_stuff();
}
You can do this easily with Google's Closure library, specifically goog.async.Deferred:
// Deferred is a container for an incomplete computation.
var ajaxFinished = goog.async.Deferred();
// ajaxCall is the asynchronous function we're calling.
ajaxCall( //args...,
function() { // callback
// Process the results...
ajaxFinished.callback(); // Signal completion
}
);
// Do other stuff...
// Wait for the callback completion before proceeding
goog.async.when(ajaxFinished, function() {
// Do the rest of the stuff...
});
You can join multiple asynchronous computations using awaitDeferred, chainDeferred, or goog.async.DeferredList.