How to Wait for Set of Asynchronous Callback Functions

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



Leave a reply



Submit