I am trying to update a global variable within a callback function (see code below)
function FunctionOne() {
var result = "";
for (var i = 0; i < 10; i++) {
AjaxFunction(function (data) {
result += data;
});
}
alert(result);
}
function AjaxFunction(callback) {
$.ajax({
url: '/test',
type: 'GET',
success: function(data) {
callback(data);
}
});
}
When I alert result it always seems to be blank. Can you guys please tell me whats wrong?
Result is not global, it is only defined within the scope of FunctionOne(). However in your example you are alerting the value, before it has been assigned as the Ajax call appears to run asynchronously. Instead try alerting after you update your value.
function FunctionOne() {
var result = "";
for (var i = 0; i < 10; i++) {
AjaxFunction(function (data) {
result += data;
alert(result); // this should have the value
}
}
alert(result); // this should be empty
}
There are two parts to your question that need to be resolved - the first relates to the asynchronous nature of AJAX and how results are returned - for that see the canonical answer to the question that this was originally marked as a duplicate of.
The second part is how to make the final answer depend on the asynchronous completion of all ten AJAX requests:
function functionOne() {
// initiate ten requests, storing the promises in an array
var def = [];
for (var i = 0; i < 10; ++i) {
def[i] = AjaxFunction();
}
// wait for all the promises to complete, with the
// eventual result also being returned as a promise
return $.when.apply($, def).then(function() {
// take a copy of the arguments
var args = [].slice.call(arguments, 0);
// concatenate them all together
return args.reduce(function(prev, current) {
return prev + current[0]; // each arg is actually a 3-element array
}, "");
});
}
function AjaxFunction() {
return $.get('/test'); // NB: using promises - no callback required
};
// NB: functionOne() is now also async - you have to wait
// for the promise to be resolved before using the result
functionOne().then(function(result) {
alert(result);
)};
See http://jsfiddle.net/alnitak/5s28G/ for a working demo.
Related
I am writing a recursive function inside for loop like below:
var output = [];
function myFunc(myValue, callback) {
myAnotherFunc(myValue, function(result){
for (var i=0; i < result.myKey.length; i++){
if(result.myKey[i].name === 'something'){
myFunc(result.myKey[i].recurseValue, function(recursiveResult){
//some recursive stuff
output.push(recursiveResult.someValue)
});
}
}
});
}
And initiating the recursive function like below:
myFunc(initialValue, function(result){
//some stuff
});
Its working fine, but how do I know when my recursive flow ends so that I can do something else from the final output?
You can use Promises™! It's basically a way to defer a callback till after an Asynchronous flow is completed: Example:
// Instead of passing your normal callback, we'll tell the
// function to use resolve(results) to pass your results to the
// next code block so it can do something after all your recursions are completed
const someTask = new Promise(resolve => myFunc(initialValue, resolve))
someTask.then(result => {
/* Do Something with the results at the end of aformentioned callback hell :D */
})
PS. You also have to modify your original function signature to:
function myFunc(myValue, callback) {
myAnotherFunc(myValue, function(result){
const cbks = [] //Store the async resuls of all myFunc() executions
for (i=0; i < result.myKey.length; i++){
if(results[i] === 'something'){
cbks.push(new Promise(res => myFunc(result[i].recurseValue, res)))
}
}
//Run all async myFunc() and return the results in an array
Promise.all(cbks).then(callback)
});
}
function myFunc(resolve) {
var rec = function(myVal, cb) {
myOther(recurseValue, function(result) {
var hasName = result[myKey].filter(function(obj) {
return obj.name === 'something';
})[0];
if (hasName) {
rec(hasName[recurseValue], function(recResult) {
// other recursive stuff
});
} else {
resolve(?); // whatever the final value should be
}
});
};
return rec;
}
function recurseAsync(f, initial) {
return new Promise(function(resolve, reject) {
f(resolve)(initial);
});
}
Couple notes.
The recurseAsync function takes a function that takes a resolution callback and returns a recursive function that calls that callback when finished to resolve the promise. myFunc has been altered to fit that format.
I used array filtering rather than a for loop and shortened some names. Also if you are using a variable for object access use [] instead of .. To use the final value when all of this is finished you can call .then on the promise.
// whatever initial value 'foo' should be
var finished = recurseAsync(myFunc, foo);
finished.then(function(finalValue) {
// do something with the final result of all the recursion
});
I have an async function like:
$scope.get_device_version = function(){
return $q(function(resolve, reject){
$http.get('/api/get_version')
.then(function(response) {
resolve(response.data.version);
},function(response) {
reject("Lost connection..");
});
});
};
Question 1: Now I want to run that function 10 times in a row sequentially, what do I do?
Question 2: Now I want to run the function until I get a desired answer from the http-request, what do I do?
Question 1
.then(callback) returns a new promise resolving to the return value of callback, or if the return value is another promise, to the settled value of that promise. This can be used to chain asynchronous calls. For example, given
function myFunction() { return promise; }
the following snippet will call myFunction 10 times sequentially
var result = $q.resolve();
for (var i = 0; i < 10; ++i) {
result = result.then(myFunction);
}
result.then(function () { /* myFunction has been called 10 times */ })
Question 2
Here, recursion is required since you don't know the exact number of times you will need to iterate. For example, given myFunction as above,
function wrapper() {
var deferred = $q.defer();
iter();
function iter(res) {
// skip first iter() call, wait for acceptable res
if (result !== void 0 && /* res is what I want */) {
return deferred.resolve(res);
}
// recurse
return myFunction().then(iter);
}
return deferred.promise;
}
wrapper().then(function (res) { /* res is an acceptable response */ })
Note that for this use case, promises do not really offer an advantage over simple callbacks.
Question 1:
var request = null;
for( var i = 0; i < 10; i++ ) {
if( request )
request.then( $scope.get_device_version );
else
request = $scope.get_device_version();
}
Question 2:
$scope.get_device_version = function(){
return $q(function(resolve, reject){
$http.get('/api/get_version')
.then(function(response) {
if( /*Not a desired answer? run again:*/ )
$scope.get_device_version()
else
resolve(response.data.version);
},function(response) {
reject("Lost connection..");
});
});
};
This is pseudo code, i have not tested it.
var checkDeviceVersion=function (times) {
$scope.get_device_version().then(function(data) {
if(times==0) return;
checkDeviceVersion(times-1);
});
}
var keepChecking=function () {
$scope.get_device_version().then(function(data) {
if(data.condition) return;
keepChecking();
});
}
Invoke then as checkDeviceVersion(10) or keepChecking()
I'm trying to do several calls inside a loop to an asynchronous API until the value that I want (true in this case) is returned. The issue is that I don't want to keep looping after that value is found and I don't want to execute the lines after the loop without the value returned from it...but so far I can't get that working. I think I'm doing something wrong here, but "I can't get the whole picture".
function isSelected(element, callback) {
// This is a little bit confusing for me...can I just get the value from
// getDataAsync without using isSelected function?
Office.select(element).getDataAsync(function (asyncResult) {
var result = true;
// some logic here that may change 'result'
callback(result);
});
}
function delete(elements) {
var index = -1;
for (var i = 0, (i < elements.length) && (index < 0); i++) {
isSelected(elements[i], function(result) {
if (result) { index = i; }; // ...and I don't want to keep "looping"
});
}
// I want to execute these lines after the for-loop is done
// ...do something with the "correct" index value
}
Have you tried Kriskowal's Q? There's a nice function called Q#allSettled:
Q.allSettled(promises)
.then(function (results) {
results.forEach(function (result) {
if (result.state === "fulfilled") {
var value = result.value;
} else {
var reason = result.reason;
}
});
});
So basically this is how it would work in your case:
var promises = [];
for(/* conditions */) {
promises.push(/* async call which returns a promise */);
}
Q.allSettled(promises).then(function(results) {
results.forEach(function (result) {
var value;
if (result.state === "fulfilled") {
value = result.value;
// do something with "value"
}
});
});
allSettled just makes sure the then will be executed regardless of whether or not the promise was successful or not, and you can check the value of the object you retrieve from your async call.
I am recommending three ways to doing it.
Using just JavaScript.
Using Async library.
Using underscore library.
Here you can see the javascript implementation:
You can do something like that:
You need to track home many times, you call the function, and how many times the callback happened
function delete(elements) {
var index = -1;
var stack=0;
for (var i = 0, (i < elements.length) && (index < 0); i++) {
stack++ // Go up on each loop
isSelected(elements[i], function() {
stack--; //Go down each callback
index = i;
if(stack==0) afterAllFinish() //When it return to 0 mean all callback have finished
});
}
function afterAllFinish(){
// I want to execute these lines after the for-loop is done
// ...do something with the "correct" index value
}
}
Using other libraries:
Please take a look at http://underscorejs.org/#after for the underscore way to solve it.
Please take a look at https://github.com/caolan/async#parallel to see the async way to solve it.
I have this code as a starting point.
// $ = jQuery
// groupAdata and groupBdata are arrays
function funcA(elem) {
for (f = 0; f < groupAdata.length ; f++) {
// this is an example on how this function calls other functions asynchronously.
elem.children('.partyA').each( function() {
this.innerHTML = "been here" + groupAdata[f];
});
}
}
function funcB(elem) {
// another function that fires more calls
for (f = 0; f < groupAdata.length ; f++) {
$.post(url, somedata, function(data) {
elem.children('.partyB').each( function() {
this.innerHTML = "will be there" + groupBdata[f] + data;
});
}
}
}
$(document).ready(function() {
$('.groupA').each(function () {
funcA(this);
});
$('.groupB').each(function (){
funcB(this);
});
});
function endofitall() {
// call this after all instances of funcA and funcB are done.
}
When running endofitall(), I'd like to be sure that all calls of funcA and funcB are done.
I take that Promises and jQuery.Deferred() would be a good/preferred approach but was not able to map the answers I found to this specific scenario. (It is part of a templating tool that fires multiple dom manipulators func[AB] for multiple DOM elements.)
You can use $.when().
Your goal should be to get to:
// call funcA, call funcB
$.when( funcA(), funcB() )
// when everything is done go on with the callback
.done(endofitall);
In the case of funcA (synchronous function there's no problem and it will work as is).
In the case of funcB (asynchronous) there are some things to consider. If it would be just one ajax call your code should be something like:
// This function returns a promise.
// When it's fulfilled the callback (in your case '.done(endofitall)')
// will be called.
function funcB(somedata){
return $.post(url, somedata);
}
As you are actually making more requests you have to return a resolved promise only when all calls have been fulfilled.
// an *Asynchronous* function, returns an array of promises
function funcB(elem, groupAdata) {
var allCalls = [];
// for each element in the array call the relative async
// function. While you're calling it push it to the array.
groupAdata.forEach(data, function(data){
allCalls.push( $.post(url, data) );
});
// allCalls is now an array of promises.
// why .apply(undefined)? read here: https://stackoverflow.com/a/14352218/1446845
return $.when.apply(undefined, allCalls);
}
At this point you can go for a flat and clear:
$.when( funcA(), funcB() ).done(endofitall);
As a rule of thumb: if you are making async requests try to always return a promise from them, this will help flatten out your code (will post some link later on if you want) and to leverage the power of callbacks.
The above code can surely be refactored further (also, I haven't used a lot of jQuery in the last few years, but the concept applies to any Js library or even when using no library at all) but I hope it will help as a starting point.
References:
$.when
A similar answer here on SO
Call endofitall() inside each iteration for funcA and funcB. Keep a counter and perform the actual work once the counter reaches the number signifying all the tasks are complete.
function funcA(elem) {
for (f = 0; f < groupAdata.length ; f++) {
// these calls are not async
elem.children('.partyA').each( function() {
this.innerHTML = "been here" + groupAdata[f];
});
endofitall();
}
}
function funcB(elem) {
// another function that fires more calls
for (f = 0; f < groupBdata.length ; f++) {
$.post(url, somedata, function(data) {
elem.children('.partyB').each( function() {
this.innerHTML = "will be there" + groupBdata[f] + data;
});
endofitall();
}
}
}
$(document).ready(function() {
$('.groupA').each(function () {
funcA(this);
});
$('.groupB').each(function (){
funcB(this);
});
});
var counter=0;
function endofitall() {
if(++counter==groupAdata.length + groupBdata.length){
//do stuff
}
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.