Reputation: 13910
I created a simple chrome extension that iterates over the users browsing history and counts the amount results that match an array of specific programming languages. The problem is I want to iterate for the length of the array and call an asynchronous chrome method for each string in the array list. The extension can be found here: What type of programmer are you? the problematic code is:
function myfunction() {
var listOfLanguages = ["Java", "C", "C++", "PHP", "C#", "Visual Basic", "Python", "Objective-C", "Perl", "Javascript", "Ruby"];
var mostPopular = "not a programmer";
var totalResults = 0;
for (var i = 0, len = listOfLanguages.length; i < len; i++) {
(function (i) {
setTimeout(function () {
chrome.history.search({
text: listOfLanguages[i],
maxResults: 500
}, function (callback) {
var countOfResults = callback.length;
var langOfResults = listOfLanguages[i];
if (countOfResults > totalResults) {
totalResults = countOfResults;
mostPopular = langOfResults;
}
if (i == 10) {
var lang = mostPopular;
console.log(mostPopular);
switch (lang) {
case "Java":
document.write('<img src="assets/images/java.png"/>');
break;
case "C#":
document.write('<img src="assets/images/C#.jpg"/>');
break;
case "C":
document.write('<img src="assets/images/C.png"/>');
break;
case "C++":
document.write('<img src="assets/images/c++.png"/>');
break;
case "Objective-C":
document.write('<img src="assets/images/objectivec.png"/>');
break;
case "Perl":
document.write('<img src="assets/images/perl.gif"/>');
break;
case "PHP":
document.write('<img src="assets/images/php.png"/>');
break;
case "Python":
document.write('<img src="assets/images/python.jpg"/>');
break;
case "Ruby":
document.write('<img src="assets/images/ruby.png"/>');
break;
case "Visual Basic":
document.write('<img src="assets/images/vb.png"/>');
default:
document.write('<img src="assets/images/noprog.png"/>');
}
}
});
}, i * 1000);
})(i);
}
}
window.onload = myfunction;
I ended up finding a work around by setting a timeout function inside each iteration, but it is not an elegant solution. I have read many articles online to handle this and they involve adding more levels of iterations which I am not understanding. Hopefully someone can explain the proper way of handling this issue, which many people are running into.
Upvotes: 0
Views: 633
Reputation: 4314
The best way to do this is to count the number of async calls that you have out and decrement as they come back.
var outstandingCalls = 0;
var resultSet = [];
for (var i = 0; i < 100; i++) {
outstandingCalls++;
someAsyncCall(function(result) {
outstandingCalls--;
resultSet.push(result);
if (outstandingCalls == 0) done();
});
}
function done() {
//done...
}
(code is unverified, but hopefully you get the idea)
You also want to make sure that your for loop has completed before calling done();
Upvotes: 1
Reputation: 37369
If I understand you correctly, something like this might work.
Take the anonymous function passed to setTimeout
and make it a named function. Now make it a recursive function by making it accept an array of languages as an argument. At the end of the function, remove the first language, then recurse with the remaining array. When the array is empty, stop recursing. Then just call this function with the original list of languages as the argument. This way, the next search iteration will only happen when the current one is finished.
Here's a chunk of pseudocode.
var search = function(languages) {
var currentLanguage = languages[0];
//...
if (languages.length > 1) {
search(languages.slice(1));
}
};
search(listOfLanguages);
EDIT: Also consider using a map instead of a switch statement. Map the language to the correct image like this:
var imageMap = {
"Objective-C": "objectivec.png",
"Visual Basic": "vb.png"
// etc...
};
Upvotes: 1