jQuery: Collecting the results from a collection of asynchronous requests
Liz and I recently spent some time building a pair stair to show how long ago people had paired with each other and one of the things we had to do was make AJAX requests to get the pairing data for each person and then collate it all to build the stair.
The original attempt to do this looked a bit like this:
var people = ["Marc", "Liz", "Ken", "Duncan", "Uday", "Mark", "Charles"];
var grid = [];
$.each(people, function(index, person) {
$.getJSON('/git/pairs/' + person, function(data) {
// parse data and create somethingCool
grid.push(somethingCool);
});
});
// do something with grid
When we try to do something with grid it is of course empty because we’ve attempted to access it before all of the callbacks (which populate it) have returned.
Pedro Teixeira has a nice blog post which explains how to solve this problem in node.js and we can use the same pattern here.
We need to write our own looping mechanism which is able to determine when the last callback has returned.
This is done by creating a copy of the people array and then manually iterating through it using shift.
var people = ["Marc", "Liz", "Ken", "Duncan", "Uday", "Mark", "Charles"];
var peopleCopy = people.slice(0), grid = [];
(function getPairs() {
var person = peopleCopy.shift();
if(peopleCopy.length == 0) {
// do something with grid
} else {
$.getJSON("/git/pairs" + person, function(data) {
// parse data and create somethingCool
grid.push(somethingCool);
getPairs();
})
}
})();
I tried to extract the asynchronous looping and ended up with the following function:
function asyncLoop(collection, seedResult, loopFn, completionFn) {
var copy = collection.slice(0);
(function loop() {
var item = copy.shift();
if(copy.length == 0) {
completionFn(seedResult);
} else {
loopFn(item, seedResult, loop);
}
})();
}
Which could be called like this:
var people = ["Marc", "Liz", "Ken", "Duncan", "Uday", "Mark", "Charles"];
asyncLoop(people, [], function(name, grid, callBackFn) {
// parse data and create something cool
grid.push(somethingCool);
callBackFn();
}, function(grid) {
// do something with grid
});
I’m not sure that it reads that much clearer but it does push some of the boiler plate code away.
About the author
I'm currently working on short form content at ClickHouse. I publish short 5 minute videos showing how to solve data problems on YouTube @LearnDataWithMark. I previously worked on graph analytics at Neo4j, where I also co-authored the O'Reilly Graph Algorithms Book with Amy Hodler.