How can I wait for set of asynchronous callback functions?
You haven't been very specific with your code, so I'll make up a scenario. Let's say you have 10 ajax calls and you want to accumulate the results from those 10 ajax calls and then when they have all completed you want to do something. You can do it like this by accumulating the data in an array and keeping track of when the last one has finished:
Manual Counter
var ajaxCallsRemaining = 10;
var returnedData = [];
for (var i = 0; i < 10; i++) {
doAjax(whatever, function(response) {
// success handler from the ajax call
// save response
returnedData.push(response);
// see if we're done with the last ajax call
--ajaxCallsRemaining;
if (ajaxCallsRemaining <= 0) {
// all data is here now
// look through the returnedData and do whatever processing
// you want on it right here
}
});
}
Note: error handling is important here (not shown because it's specific to how you're making your ajax calls). You will want to think about how you're going to handle the case when one ajax call never completes, either with an error or gets stuck for a long time or times out after a long time.
jQuery Promises
Adding to my answer in 2014. These days, promises are often used to solve this type of problem since jQuery's $.ajax()
already returns a promise and $.when()
will let you know when a group of promises are all resolved and will collect the return results for you:
var promises = [];
for (var i = 0; i < 10; i++) {
promises.push($.ajax(...));
}
$.when.apply($, promises).then(function() {
// returned data is in arguments[0][0], arguments[1][0], ... arguments[9][0]
// you can process it here
}, function() {
// error occurred
});
ES6 Standard Promises
As specified in kba's answer: if you have an environment with native promises built-in (modern browser or node.js or using babeljs transpile or using a promise polyfill), then you can use ES6-specified promises. See this table for browser support. Promises are supported in pretty much all current browsers, except IE.
If doAjax()
returns a promise, then you can do this:
var promises = [];
for (var i = 0; i < 10; i++) {
promises.push(doAjax(...));
}
Promise.all(promises).then(function() {
// returned data is in arguments[0], arguments[1], ... arguments[n]
// you can process it here
}, function(err) {
// error occurred
});
If you need to make a non-promise async operation into one that returns a promise, you can "promisify" it like this:
function doAjax(...) {
return new Promise(function(resolve, reject) {
someAsyncOperation(..., function(err, result) {
if (err) return reject(err);
resolve(result);
});
});
}
And, then use the pattern above:
var promises = [];
for (var i = 0; i < 10; i++) {
promises.push(doAjax(...));
}
Promise.all(promises).then(function() {
// returned data is in arguments[0], arguments[1], ... arguments[n]
// you can process it here
}, function(err) {
// error occurred
});
Bluebird Promises
If you use a more feature rich library such as the Bluebird promise library, then it has some additional functions built in to make this easier:
var doAjax = Promise.promisify(someAsync);
var someData = [...]
Promise.map(someData, doAjax).then(function(results) {
// all ajax results here
}, function(err) {
// some error here
});
Wait for asynchronous callback function inside synchronous one
The pattern:
result = asyncDoSomething(...);// returns value
is not possible. The async function just returns immediately and result gets undefined, as you have found out. You can't return a value from an async fcn
The classical design for getting results from an async call is by a callback, which seems convoluted and, if you have nested callbacks, can become horrible in a hurry.
Promises were developed for just this problem. Promises partly solve this problem by allowing you to write code that looks like the following PSEUDO code
promise( executes async fcn and passes result to 'then')
.then( myFcn(asynResult){use result})
.then(...
While not ideal, this allows you to write a form of sequential async code. If you haven't already, I encourage you to spend some time with Promises.
The javascript community is still hoping that a future version of JS will allow something like:
result = wait for asyncFunction(...)
But it doesn't seem to be on the horizon yet.
Wait for the async callback function to return in java
You can use a FutureTask
that gets called in your callback function:
final FutureTask<Object> ft = new FutureTask<Object>(() -> {}, new Object());
final Function<Boolean, ? extends Class<Void>> functionCallback = (Boolean t) -> {
if(t) {
plugin.setIsInstalled(Boolean.TRUE);
ft.run();
}
return Void.TYPE;
};
foo.install(plugin,functionCallback);
ft.get();
if(plugin.getIsInstalled().getValue())
return "done";
else
return "not done";
Future.get
waits till the run
method was called, you can also use the get
-method that accepts a timeout so you can react on that if it takes too long.
Wait for async function with callback
You can return a Promise
which contains the array using Promise.all
. The parameter to Promise.all
must be an array of promises, and it returns a promise of an array of values.
You can use it like this:
function makeDataArray(dataFiles) {
return Promise.all(dataFiles.map(function (dataFile) {
return new Promise(function (resolve, reject) {
package.magicMethodToParseData(dataFile, function (error, result) {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
}));
}
makeDataArray(dataFiles)
.then(doSomethingWithData)
.catch(console.error);
Wait for multiple non nested callbacks
The easiest would be to just keep flags about the callbacks state:
let count = 0;
function callback() {
if(++count === 2) op3();
}
op1(callback); op2(callback);
But in the long term it might be beneficial to let the operations return promises, e.g.:
async function op1() {
// Do some async stuff and *await* that
return "something";
}
then you could just do:
Promise.all(op1(), op2()).then(op3);
How can I wait until Asynchronous callbacks are finished before using the data retrieved?
In your code, you're calling getData()
and then on the next line you iterate over the test
array. This happens in the same pass of the event loop so you're not giving your promises any chance to actually run their logic and resolve to anything. Line-by-line execution is synchronous and no operations can get executed in between.
A solution is to return a promise from the getData
function and wait for it to resolve.
Since you're working with Promises you also don't need any callback to the $getJSON
function.
$(document).ready(function() {
var channels = [
"freecodecamp", "storbeck", "terakilobyte", "habathcx",
"RobotCaleb", "thomasballinger", "noobs2ninjas", "beohoff",
"ESL_SC2", "brunofin"
];
var test = [];
function getData() {
var promises = [];
for (var i = 0; i < channels.length; i++) {
promises.push(
$.getJSON(
'https://api.twitch.tv/kraken/streams/'+channels[i]+'?callback=?'
)
);
}
return $.when.apply($, promises).then(function() {
for(var j=0; j<promises.length; j++){
test.push(promises[j]);
}
});
}
getData().then(function() {
console.log("do I have data?: ");
for(var j=0; j<test.length; j++) {
console.log(test[j].responseJSON);
}
});
});
Let me also suggest a refactoring of this code. This is an interesting question and a common thing to do in JavaScript and I'd like demonstrate how to work with arrays of promises using this example.
Each asynchronous operation runs independently and your instinct was correct: you need some additional tool to put all the results together.
When using promises, this tool is Promise.all
. In fact, jQuery's $.when
is similar although I find Promise.all
easier to work with due to its inputs and return types. It's also standard JavaScript and is available in all modern browsers.
Promise.all
can be used to run some logic when a number of individual promises resolve. Formally, Promise.all
takes an array of promises and returns a single promise which resolves when all of the input promises resolve (or one of them rejects). it resolves to an array of resolved values to which individual promises resolved to.
Since Promise.all
takes an array, it works great with array methods like map
and filter
.
function getStreams(channels) {
// Transform an array of channel names into an array of Promises
// representing the asynchronous action of getting the info
// about each channel.
const promises = channels.map(
channel => $.getJSON(
'https://api.twitch.tv/kraken/streams/'+channel+'?callback=?'
)
);
// Return a new promise which will resolve when *all* channels
// have been queried.
return Promise.all(promises);
}
const channels = [
"freecodecamp", "storbeck", "terakilobyte", "habathcx",
"RobotCaleb", "thomasballinger", "noobs2ninjas", "beohoff",
"ESL_SC2", "brunofin"
];
getStreams(channels).then(
// infos is an array of resolved data about the channels
infos => infos.forEach(
info => console.log(info)
)
);
How to await for a callback to return?
async/await
is not magic. An async function is a function that can unwrap Promises for you, so you'll need api.on()
to return a Promise for that to work. Something like this:
function apiOn(event) {
return new Promise(resolve => {
api.on(event, response => resolve(response));
});
}
Then
async function test() {
return await apiOn( 'someEvent' ); // await is actually optional here
// you'd return a Promise either way.
}
But that's a lie too, because async functions also return Promises themselves, so you aren't going to actually get the value out of test()
, but rather, a Promise for a value, which you can use like so:
async function whatever() {
// snip
const response = await test();
// use response here
// snip
}
How do I simultaneously call async functions and wait for all callbacks?
You cannot return an asynchronously retrieved result in a synchronous way. So the function main cannot return the results. You should stick to the asynchronous method you have chosen. So with callbacks, you should also provide a callback function when invoking main:
function main(callback){
var all_results = [];
function collect(results) {
all_results.push(results);
// when all 3 calls are complete:
if (all_results.length === 3) callback(all_results);
}
f1(collect);
f2(collect);
f3(collect);
}
Call like this:
main(function (all_results) {
console.log(all_results);
});
As others have stated, promises lead to nicer code in such situations.
How to do it with promises
Let's say you cannot modify functions f1, f2, f3, then you could create promisified versions of them with one simple helper function. Once that is done, the ES6-included method Promise.all
will do the rest of the job, so the code becomes quite concise.
Here is a snippet that defines some dummy functions f1, f2, f3 which use setTimeout
to get the asynchronous effect:
function promisify(f) { return new Promise(f);}
function main(){ return Promise.all([f1, f2, f3].map(promisify));}
main().then(function (results) { console.log(results);});
function f1(callback){ setTimeout(function () { callback('result from f1'); }, 100);}
function f2(callback){ setTimeout(function () { callback('result from f2'); }, 500);}
function f3(callback){ setTimeout(function () { callback('result from f3'); }, 200);}
Related Topics
How Does Facebook Disable the Browser's Integrated Developer Tools
Multiple Assignment in JavaScript? What Does [A,B,C] = [1, 2, 3]; Mean
Detect Ie Version (Prior to V9) in JavaScript
Mongoose and Multiple Database in Single Node.Js Project
How to Getelementbyclass Instead of Getelementbyid with JavaScript
Is There a Null-Coalescing (Elvis) Operator or Safe Navigation Operator in JavaScript
Variable as the Property Name in a JavaScript Object Literal
Difference Between Dates in JavaScript
Code Within D3.JSON() Callback Is Not Executed
Javascript: Passing Parameters to a Callback Function
Using "Object.Create" Instead of "New"
Why Is My Function Call That Should Be Scheduled by Settimeout Executed Immediately
Node.Js Plans to Support Import/Export Es6 (Ecmascript 2015) Modules
What Is the Meaning of the 'G' Flag in Regular Expressions
Maximum Size of an Array in JavaScript
Why Don't We Just Use Element Ids as Identifiers in JavaScript