0

I'm trying to iterate through an array of RSS feeds as below:

        var rssFeeds = [ ['http://www.huffingtonpost.com/tag/womens-rights/feed/', "Huffington Post"], ['http://abcnews.go.com/topics/urss?pageid=681900', "ABC News"], ['http://www.globalissues.org/news/topic/166/feed', "Global Issues"], ['http://topics.nytimes.com/top/reference/timestopics/subjects/f/feminist_movement/index.html?rss=1', "The New York Times"] ];            

This array contains both the location of the feed and a string with the name of the feed. I iterate through the array with a for loop as below, the one with i as an iterator.

Once I receive the results, I iterate through the results in the callback function with the for loop j. I append each fetched result to another array entryArray, and after appending each result, I add a new attribute 'source' to that fetched result using the name for the RSS feed.

        function loadEntries()
        {
            var feedr = new Array();

            for( var i = 0; i < rssFeeds.length; i++ )
            {
                feedr[i] = new google.feeds.Feed( rssFeeds[i][0] );
                feedr[i].setNumEntries(loadAmount);

                feedr[i].load(function(result) {
                    if (!result.error) {
                        console.log(i);
                        for (var j = 0; j < result.feed.entries.length; j++) {
                            entryArray[entryArray.length] = result.feed.entries[j];
                            entryArray[entryArray.length - 1]['source'] = rssFeeds[i][1];
                        }
                    }
                });
            }
        } 

However, and this is where the problem arises, the iterator I use (i) to indicate the name to append is always equal to rssFeeds.length, because the callback functions for all four load commands occur after the initial for loop has already finished iterating. The console.log(i); you see always returns 4.

This worked when I copied and pasted the code for each item individually, but I'd rather not copy and paste because the RSSFeeds array will probably be much longer in the future. Is there any way I can accomplish this with a loop?

user2884505
  • 525
  • 1
  • 6
  • 19
  • Try adding a `.bind(i)` on your function end: `}.bind(i));` – juvian Oct 22 '14 at 20:05
  • Have you tried some of those: http://stackoverflow.com/questions/4288759/asynchronous-for-cycle-in-javascript ? – nd_macias Oct 22 '14 at 20:08
  • Also I've found this solution, which i belive should do the trick: http://www.javascriptcookbook.com/article/Preserving-variables-inside-async-calls-called-in-a-loop – nd_macias Oct 22 '14 at 20:12
  • What does feedr[i].load do? If it's async, the loop will continue to iterate until complete, then when the load callback is finally executed, i will have been incremented a few times... – BrMcMullin Oct 22 '14 at 20:13

2 Answers2

0

Consider the following JSFiddle example

The reason your code does not function as intended is because the value of the variable i, will be i = 4 at the end of the loop and not 0,1,2,3 as desired because this would already have happened been incremented by the for loop.

The trick is to use a recursive function and your load function will function similarly to:

    feedr.load(function (result) {
        if (!result.error) {
            console.log(i);
            for (var j = 0; j < result.feed.entries.length; j++) {
                entryArray[entryArray.length] = result.feed.entries[j];
                entryArray[entryArray.length - 1]['source'] = rssFeeds[i][1];
            }
            if (rssFeeds.length - 1 > i) {
                loadEntries();
                i++;
            }
        }
     });
Gregory Nikitas
  • 493
  • 2
  • 7
  • The problem with this is that the order I start the load methods in is not necessarily the order I get the entries back in. For instance, I might start them in the order 1, 2, 3, and 4, but RSS feed number 2 might be smaller and load before the other 3. In that case, I want it to be assigned to rssFeeds[1][1], not rssFeeds[0][1]. – user2884505 Oct 23 '14 at 00:51
  • I'm quite sure this one will load in order as desired because it waits for the previous entry to have loaded. A for loop would give the behaviour you describe in your comment. – Gregory Nikitas Oct 23 '14 at 06:01
0

So first of all, I'd like to thank nd_macias for providing me with the links that helped me find this solution. Basically, I wrapped the load function in a function, and then called that function with the for loop as below:

        function loadEntries()
        {
            var feedr = new Array();

            for( var i = 0; i < rssFeeds.length; i++ )
            {
                feedr[i] = new google.feeds.Feed( rssFeeds[i][0] );
                feedr[i].setNumEntries(loadAmount);

                var f = function(n) {
                    feedr[n].load(function(result) {
                        if (!result.error) {
                            for (var j = 0; j < result.feed.entries.length; j++) {
                                entryArray[entryArray.length] = result.feed.entries[j];
                                entryArray[entryArray.length - 1]['source'] = rssFeeds[n][1];
                            }
                        }
                    });
                }

                f(i);
            }
        } 
user2884505
  • 525
  • 1
  • 6
  • 19