AJAX with callback function - javascript

How can we make the asynchronous function stop and wait for the response?
I got some solution about getting the value from asynchronous functions from How do I return the response from an asynchronous call? But, I still have more questions about how can we get a value from asynchronous function if we have to pass in some values to the function.
Here, I have 3 functions. First, the asynchronous function.
function getVal(param,callbackFunc)
{
a = function()
{
if(a.state==finished)
callbackFunc(a.response);
};
}
This one is the call back function
handler(result)
{
return result;
}
And, This is where I call the asynchronous function, and also where I got the problem
function core()
{
for(var i = 0 ; i < 10 ; i ++)
{
getVal(g[i],handler);
}
}
As you can see, the parameter I sent to the getVal function is an array. So, the question is how can we make the getVal function execute the parameters synchronously.
This always give me the result of the last member of array (It seems like the function skips all values from g[0] to g[9].it always returns the value of getVal(g[10],handler)) .
Please help
Thank you.

Your problem is not directly related to Ajax, it occurs whenever the execution of a code block is delayed. The following code snippets reproduce this situation using a timer. The last code snippet is a possible solution to your problem. By the way, if you still feel confused about Ajax, here is an attempt to make it easy to understand: https://stackoverflow.com/a/32163917/1636522.
No delay: i is printed immediately.
print('loop start');
for (var i = 0; i < 3; i++) {
print(i);
}
print('loop end');
function print (html) {
document.body.innerHTML += html + '<br />';
}
1 second delay: The print action is executed 1 second later, but the "for" loop keeps going, hence, i reaches 3 before being printed.
print('loop start');
for (var i = 0; i < 3; i++) {
setTimeout(function () {
print(i);
}, 1000);
}
print('loop end');
function print (html) {
document.body.innerHTML += html + '<br />';
}
1 second delay + persistence: i is copied to a new variable. This variable is local to the self-invoking function and still alive when the print function is called.
print('loop start');
for (var i = 0; i < 3; i++) {
(function (copy) {
setTimeout(function () {
print(copy);
}, 1000);
})(i);
}
print('loop end');
function print (html) {
document.body.innerHTML += html + '<br />';
}

One way to do that is by making ajax request synchorous.
var xhr =new XMLHttpRequest();
xhr.open("GET","url", false);
False here will make sure rest of the code isnt ran until havent gotten response.
Otherwise what i do is make all async requests then upon every response subtract 1 from array.length and check if it's zero. If it is then it means we have gotten all of the responses so then call the callback.

Related

How can I update a window object in Firefox Developer Tools?

I wanna use Chrome or Firefox Developer Tools to execute code on a website.
When I execute a "window.RR" variable lonely, it shows me the right value (it is a measure of the site's server time (in milliseconds) and every time I execute it gives me a different value). When I use it in a loop (variable c), then the variable is constant in all loops and it equals to the value of the first loop.
My code is:
var i;
b=window.RR;
for (i=0 ; i<400000 ; i++) {
c=window.RR;
if (c!==b) {
alert(c)
}
}
I expect to receive the correct value when I use it in the loop, how can I achieve this?
I assume the the window.RR variable is updated by an interval. This means that the variable is updated asynchronous.
For example, lets say that the window.RR holds the timestamp (in milliseconds) of the server, and the code below updates the window.RR every millisecond.
setInterval(function () {
window.RR += 1;
}, 1);
If you then run a loop, that may take more than 1ms to be executed
var c = window.RR;
for (var i = 0; i < 999999999999999; i++){
if (c != window.RR) { // This is always false
console.log('This will never be printed');
}
}
the window.RR will not change during the execution of the loop, because the javascript asynchronous codes does not run in parallel.
Thus, if the loop takes more than 1 ms to be executed window.RR update code will happen after the loop finish (it will have to wait any other code fired to finish before that code can be executed).
More info:
This code does not let any other code to run while the for loop is running.
var c = window.RR;
for (var i = 0; i < 999999999999999; i++){
if (c != window.RR) { // This is always false
console.log('This will never be printed');
}
}
In order to let other code to run, you will have to make each loop async.
var c = window.RR;
var loop = function(i) {
if (i < 999999999999999) {
i++;
if (c != window.RR) {
console.log('This will be printed!');
}
else {
// Allow other javascript codes to run
// So that the window.RR can be updated
setTimeout(function () {loop(i);}, 0);
}
}
};
loop(0);

setTimeout and passing variables to anonymous function together [duplicate]

This question already has answers here:
setTimeout in for-loop does not print consecutive values [duplicate]
(10 answers)
Closed 8 years ago.
i'm writing a code and i've stuck with setTimeout function. I would like to get animation effect. Display elements of array with delay. Code is done.
for (var i=0;i <= array.length-1;i++) {
(function(el) {
setTimeout(function(){
document.getElementById('Result').innerHTML += Math.floor(el);
console.log(Math.floor(el));
}, 3000*(i+1));
})(array[i]);
I had problem with delay when i use for (var i=array.length-1; i>=0;i--) Why? (The idea of this code is display items array form the last to the first)
for (var i=0;i <= array.length-1;i++) {
(function(el) {
setTimeout(function(){
Give now the same resultat as: for (var i=array.length-1; i>=0;i--) {
console.log(array[i]+'!')
The problem here is about closures. A closure is a type of anonymous function used to remember the arguments for using them later in asynchronous code (e.g. in functions called by setTimeout).
If you write something like:
setTimeout(function(foo){...}(value), 3000)
the function gets called before calling setTimeout. You have to use a closure to do this:
(function(foo){
setTimeout(function() {
... do something with foo ...
}, 3000);
})(value_of_foo);
This code will remember the value of foo creating a setTimeout with a function that uses it.
Another problem is that you have to increase the setTimeout time to create a delay, otherwise the for will create a bunch of setTimeout(..., 3000) that will be executed all at once. So what you will need to do to your code is something like the following:
for (var i = 0; i <= array.length; i++) {
(function(el) {
setTimeout(function(){
document.getElementById('Result').innerHTML += Math.floor(el)
}, 3000 * i);
})(array[i]);
}
Timeouts don't execute until after your main function has finished, this means that by the time your timeout function executes per loop, your i variable will be at its final value (in this case = 0). Your function declaration is also incorrect as the timeout function does not pass in those parameters for you. To do this, you need to wrap your timeout inside of another function call that takes in the parameters for the current loop, try something like this instead...
for (var i=array.length-1; i>=0;i--) {
function(array, i) {
setTimeout(function() {
document.getElementById('Result').innerHTML += Math.floor(array[i]);
}, (i+1) * 3000);
}(array, i);
}
First of all you are immediately calling the function and its result is assigned as the callback of the timeout. (this is wrong as the first argument must be a function)
Then, you have some syntax errors with missing closing } and )..
Try
for (var i=array.length-1; i>=0;i--) {
function(array,i){
setTimeout(function(){
document.getElementById('Result').innerHTML += Math.floor(array[i]);
}, 3000*i);
}(array,i);
}
I used 3000*i so that each timeout is 3 seconds after the other

Callback execution sequence in Javascript, Retrieving from IndexeddB

If I've a code like this:
testJSCallbacks();
function testJSCallbacks(){
var i = 0;
for (i = 0; i < 5; i++){
console.log("Step 1 "+i);
foo(i, myCB);
}
}
function foo(key, fnCB){
//retrieve png image blob from indexedDB for the key 'key'. Assume that the database is
//created and started properly
var getRequest = transaction.objectStore("store").get(key);
getRequest.onsuccess = function (event) {
var result = event.target.result;
if(result){
console.log("Step 2 " + key + " Found");
}else{
console.log("Step 2 " + key + " not Found"); //for the same 'key' value this happens before the above result is valid. i.e. key found
}
fnCB(result);
}
}
myCB = function (result){
console.log("Step 3 "+result);
}
Actual Output (just for example):
Step 1 0
Step 1 1
Step 1 2
Step 1 3
Step 1 4
Step 2 0 Found
.
.
Step 3 <result>
.
.
.
Desired output:
Step 1 0
Step 2 0 Found
Step 3 <result value of key 0 goes here>
In my code I'm trying to read png blobs from IndexedDB, which are already stored earlier. But while reading/searching for a specific blob it takes too long to get the result back, meanwhile a search for second blob occurs even though the earlier search is not finished yet.
Can anyone advise what/how would you do if you need to call an asynchronous function in a loop multiple times and the callback takes too long to come? Is my code correct and makes logical sense or this isn't how javascript is done? I'm very new to this and come from an embedded C background.
The problem is the getRequest.onsuccess function is asynchronous, while the for loop executes synchronously. This is why it finishes first... In fact, while you are executing the testJsCallbacks, nothing else will execute until the current execution context ends and control is returned back to the javascript event queue because javascript execution context within the browser is single threaded.
To do what you desire, I would suggest using a promise library. Then you can write code like this (see jsfiddle which uses Q.js library):
testJSCallbacks();
function testJSCallbacks(){
var i = 0,
promise;
for (i = 0; i < 5; i++) {
//Make initial promise if one doesn't exist
if (!promise) {
promise = Q.fcall(getStep(i));
}
//Append to existing promise chain
else {
promise = promise.then(getStep(i));
}
//then function returns another promise that can be used for chaining.
//We are essentially chaining each function together here in the loop.
promise = promise.then(function (key) {
//Log the output of step here
console.log("Step 1 " + key);
return key;
})
//then function takes a callback function with one parammeter (the data).
//foo signature meets this criteria and will use the resolution of the last promise (key).
.then(foo)
//myCB will execute after foo resolves its promise, which it does in the onsuccess callback
.then(myCB);
}
}
function getStep(step) {
return function () {
return step;
}
}
function foo(key) {
//retrieve png image blob from indexedDB for the key 'key'. Assume that the database is
//created and started properly
var getRequest = transaction.objectStore("store").get(key),
//Need to return a promise
deferred = Q.defer();
getRequest.onsuccess = function (event) {
var result = event.target.result;
if(result){
console.log("Step 2 " + key + " Found");
}else{
console.log("Step 2 " + key + " not Found"); //for the same 'key' value this happens before the above result is valid. i.e. key found
}
deferred.resolve(result);
}
return deferred.promise;
}
function myCB (result){
console.log("Step 3: " + result);
}
The jsfiddle uses a setTimeout instead of objectStore to demonstrate the async nature.
Explaining getStep function:
getStep function is like a "seed" function in that it kicks off resolving the chain of what you want to do (i.e. Step 1, Step 2, Step 3). It simply creates a function that returns the value of the variable passed in. This is used to pass into the function that console.logs Step 1 in the promise resolution chain and then returns the value for the next promise resolution (Step 2)... JavaScript has the concept of closures and in order to get the correct value for step number (instead of the value of 'i' at the time when the callbacks are executed) we needed to create a closure for the variable i.
To demonstrate, consider this code:
HTML:
<button type="button">0</button>
<button type="button">1</button>
<button type="button">2</button>
<button type="button">3</button>
<button type="button">4</button>
addHandlers();
function addHandlers() {
//Don't do this. Just demonstrating a feature:
var buttons = document.getElementsByTagName("button") || [],
i, len = buttons.length;
for (var i = 0; i < len; i++) {
buttons[i].onclick = function () {
//will always alert 5
alert(i);
}
}
}
Since the variable i is 5 after the for loop ends, this is the value that is used in the function. This is why you would need to create a closure for i (using getStep again for clarity):
addHandlers();
function addHandlers() {
var buttons = document.getElementsByTagName("button") || [],
i, len = buttons.length;
for (var i = 0; i < len; i++) {
//getStep creates a function with a closure for i at its current value in the loop
buttons[i].onclick = getStep(i);
}
}
function getStep(i) {
return function () {
alert(i);
}
}
Fiddle for before and after.

jQuery delay between for loop iterations

Is that possible to set delay between the javascript for loop NOT using only settimeout but based on when an specific ajax call is done?
something like:
for (var i = 0 ; i < list.length ; i++)
{
$when('index.jsp').done(function(a1){
alert(i);
});
}
Say ajax request is sent and the first alert comes up, then the second iteration and alert is performed when the first ajax call is done.
I think the following should work:
(function() {
var i = 0, end = list.length;
function iterate() {
$.ajax('index.jsp').done(function() {
alert(i);
i++;
if (i < end) {
iterate();
}
});
}
iterate();
})();
An easy way to accomplish what you're after would be to use a callback in the Ajax function to recursively call itself. Otherwise I'm not sure of a way to do it in a for loop because Ajax calls are asynchronous.

$.get in a loop: why the function is performed after incrementing?

I am a beginner in Javascript and I feel that there is something wrong with me about the $.get jQuery.
Normally, you can assign it to a function that will execute after the data is retrieved correctly.
But if I put my $.get in a loop, the loop continues to execute even if the data is not yet retrieved, and here is my problem.
Here is my code (this is for GreaseMonkey):
var1 = document.getElementsByClassName("some_class");
i = 0;
while (i < var1.length) {
url = var1[i].getElementsByTagName("some_tag")[0].href;
$.get(url, function(data) {
if (data.contains("some_string")) {
alert(i);
}
});
i++;
}
Here, the alert returns var1.length event if it should returns 1 for exemple.
I try to put an alert(i) just after the url declaration and I understood that i++ was done before the function in my $.get.
This is surely a trivial problem, but I can not grasp the logic to not make this happen.
Wrap your $.get function thus:
(function(i) {
$.get(url, function(data) {
if (data.contains("some_string")) {
alert(i);
}
});
})(i);
The immediately invoked function expression causes the current value of i that's in the outer scope to be bound via the function's parameter i (which then hides the outer variable). If you like, give the function parameter a different name.
Note that this only fixes the problem you actually stated, which is that the loop variable is incremented independently of the callbacks. If you wish to ensure that the AJAX requests run one at a time then there are other solutions, e.g.:
var els = document.getElementsByClassName("some_class");
var i = 0;
(function loop() {
if (i < els.length) {
var el = els[i];
var url = el.getElementsByTagName("some_tag")[0].href;
$.get(url).done(function(data) {
if (data.contains("some_string")) {
alert(i);
}
i++;
}, loop); // .done(f1, f2) - see below
}
})();
The .done() call is in the form .done(callback, loop) and the two functions will be called in order. So the i++ line always happens first, and then it arranges for loop to be called pseudo-recursively to process the next element.
Since you're using jQuery, you can simplify your code quite a bit:
$('.some_class').each( function( i, element ) {
var url = $(element).find('some_tag')[0].href;
$.get( url, function( data ) {
if( data.contains("some_string") ) {
alert( i );
}
});
});
Changes from the original code are:
jQuery calls instead of the getElementsBy* functions.
jQuery .each() for the loop.
Added missing var where needed. (Very important in any version of the code!)
Note that the use of .each() automatically gives you the same effect as the immediately invoked function expression (IIFE) in another answer, but without the extra complication. That's because .each() always uses a callback function, and that creates the closure needed to preserve the i variable (and element too) uniquely for each iteration of the loop.
You can also do this when you have an ordinary while or for loop, and you still don't need the IIFE. Instead, simply call a function in the loop. Written this way, the code would be:
var $elements = $('.some_class');
for( var i = 0; i < $elements.length; i++ ) {
checkElement( i, $elements[i] );
}
function checkElement( i, element ) {
var url = $(element).find('some_tag')[0].href;
$.get( url, function( data ) {
if( data.contains("some_string") ) {
alert( i );
}
});
}
As you can see, the checkElement function is identical to the .each() callback function. In fact, .each() simply runs a similar for loop for you and calls the callback in exactly the same way as this code. Also, the for loop is more readable than the while loop because it puts all the loop variable manipulation in one place. (If you're not familiar with the for loop syntax it may seem less readable at first, but once you get used to it you will probably find that you prefer the for loop.)
In general, when tempted to use an IIFE in the middle of a loop, try breaking that code out into a completely separate function instead. In many cases it leads to more readable code.
Here's a little demo for you to investigate further.
$("#output").empty();
var startTime = new Date().getTime();
// try experimenting with async = true/false and the delay
// don't set async to false with too big a delay,
// and too high a count,
// or you could hang your browser for a while!
// When async==false, you will see each callback respond in order, followed by "Loop finished".
// When async==true, you could see anything, in any order.
var async = true;
var delay = 1;
var count = 5;
function createClosure(i) {
// return a function that can 'see' i.
// and i's remains pinned within this closure
return function (resp) {
var duration = new Date().getTime() - startTime;
$("#output").append("\n" + i + " returned: " + resp + " after " + duration + "ms");
};
}
for (var i = 0; i < count; i++) {
// jsfiddle url and params
var url = "/echo/html/";
var data = {
html: "hello " + i,
delay: delay
};
$.ajax(url, {
type: "post",
data: data,
async: async
}).then(createClosure(i));
}
var duration = new Date().getTime() - startTime;
$("#output").append("\n" + "Loop finished after " + duration + "ms");
Sample async=true output:
Loop finished after 7ms
0 returned: hello 0 after 1114ms
1 returned: hello 1 after 1196ms
2 returned: hello 2 after 1199ms
4 returned: hello 4 after 1223ms
3 returned: hello 3 after 1225ms
Sample async=false output (and the browser hangs for 5558ms!):
0 returned: hello 0 after 1113ms
1 returned: hello 1 after 2224ms
2 returned: hello 2 after 3329ms
3 returned: hello 3 after 4444ms
4 returned: hello 4 after 5558ms
Loop finished after 5558ms

Categories

Resources