简体   繁体   中英

How to capture results from end of FOR loop with Nested/Dependent APIs calls in Node JS

This is my first JavaScript & Node project and I am stuck….

I am trying to call a REST API that returns a set of Post IDs... and based on the set of retrieved IDs I am trying to call another API that returns details for each ID from the first API. The code uses Facebook API provided by Facebook-NodeSDK.

The problem I am having is that the second API fires of in a FOR Loop…. As I understand the for loop executes each request asynchronously…. I can see both the queries executing however I can't figure out how to capture the end of the second for loop to return the final result to the user…

Following is the code…

exports.getFeeds = function(req, res) {

    var posts = [];
    FB.setAccessToken(’SOME TOKEN');
    var resultLength = 0;


     FB.api(
        //ARG #1 FQL Statement
        'fql', { q: 'SELECT post_id FROM stream WHERE filter_key = "others"' },
        //ARG #2 passing argument as a anonymous function with parameter result
        function (result)
           {

               if(!result || result.error) {
                    console.log(!result ? 'error occurred' : result.error);
                    return;
                } //closing if handling error in this block

                    var feedObj
                    console.log(result.data);
                    console.log(result.data.length);

                        for (var i = 0; i<resultLengthj ; i++) {

                        (function(i) {
                            feedObj             = {};
                            FB.api( result.data[ i].post_id,  { fields: ['name', 'description', 'full_picture' ] },
    //                          fbPost is data returned by query
                                function (fbPost) {
                                    if(!fbPost || fbPost.error) {
                                        console.log(!fbPost ? 'error occurred' : result.error);

                                        return;
                                    }
    //                                else
                                        feedObj=fbPost;
                                        posts.push(feedObj);
                            });
                       })(i);
                    }// end for

           }//CLOSE ARG#2 Function

    );// close FB.api Function

NOTE I need to call…... res.Send(post)…. and have tried to call it at several places but just can't get all the posts… I have removed the console statements from the above code…which have shown that the data is being retrieved...

Thanks a lot for your help and attention....

If you just stick res.send almost anywhere in your code, it will be certain to get called before your posts have returned.

What you want to do in a case like this is to declare a counter variable outside your for loop and set it to zero. Then increment it inside the for loop. Then inside your inner callback (in your case, the one that is getting called once for each post), you would decrement the counter and test for when it hits zero. Below I apply the technique to an edited version of your code (I didn't see what feedObj was actually doing, nor did I understand why you were using the immediately-invoked function, so eliminated both - please let me know if i missed something there).

var posts = [];
FB.setAccessToken(’SOME TOKEN');
var resultLength = 0;


FB.api(
    //ARG #1 FQL Statement
    'fql', { q: 'SELECT post_id FROM stream WHERE filter_key = "others"' },
    //ARG #2 passing argument as a anonymous function with parameter result
    function (result)
    {

        if(!result || result.error) {
            return;
        } //closing if handling error in this block

        var counter = 0;
        for (var i = 0; i<resultLengthj ; i++) {

            counter++;
            FB.api( result.data[ i].post_id,  { fields: ['name', 'description', 'full_picture' ] },
                function (fbPost) { // fbPost is data returned by query
                    if(!fbPost || fbPost.error) {
                        return;
                    }
                    posts.push(fbPost);
                    counter--;
                    if (counter === 0){
                        // Let's render that page/send that result, etc
                    }
                });

        }// end for

    }//CLOSE ARG#2 Function

);// close FB.api Function

Hope this helps.

Essentially you are wanting to do an async map operation for each id.

There is a really handy library for doing async operations on collections called async that has a async.map method.

var async = require('async'); 

FB.api(
    'fql', { q: 'SELECT post_id FROM stream WHERE filter_key = "others"' },
    //ARG #2 passing argument as a anonymous function with parameter result
    function (result) {

        async.map(
            result,

            function (item, callback) {
                FB.api(
                    item.post_id, 
                    { fields: ['name', 'description', 'full_picture' ] }, 
                    callback
                );
            },

            function (err, allPosts) {
                if (err) return console.log(err);

                // Array of all posts
                console.log(allPosts);
            }
        );

    }
);

You definitely don't need to use this, but it simplifies your code a bit. Just run npm install --save async in your project directory and you should be good to go.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM