2

I am calling a ajax request inside forEach loop but the problem is that by the time the response is received from ajax, the loop is finished(if I am not wrong)

forEach Loop:

var retrievedContacts = {};
var retrievedContactsArr = [];
contacts.getContacts(function (err, contacts) {
    contacts.forEach(function (entry) {
        if (entry.phoneNumber !== '') {
            retrievedContacts = {
                contact: {
                    "address": {
                        "home": "",
                        "office": ""
                    }
                },
                "profileData": getPhotos(entry.photo, req.token)
            };
            retrievedContactsArr.push(retrievedContacts);
        }
    });
});

Function call is this line "profileData": getPhotos(entry.photo, req.token) in the above code.

Function:

function getPhotos(url, token){
    var base64Image = '';
    getApiResponse(url+"?access_token="+token,"", function (res1) {
        if (res1.error) {
            console.log('Could not fetch google photos......', res1.error);
        } else {
            base64Image = new Buffer.from(res1.body).toString('base64');
            console.log('base64 is.........................', base64Image);
        }
    });
    return base64Image;
}

Ajax call:

function getApiResponse(url, params, next) {
    unirest.get(url)
    .query(params)
    .timeout(60000)
    .end(function (res) {
        if (next)
            next(res);
    });
}

I am able to print the response but unable to return it to the calling function. The value of "profileData" is empty string. How do I handle this?

kittu
  • 6,662
  • 21
  • 91
  • 185
  • 1
    What is expected return value from `getContacts()` calls? Is `getApiResponse` synchronous? – guest271314 Jan 02 '17 at 15:59
  • @guest271314 return value is a byteArray. I don't think `getApiResponse' is synchronous. I didn't make synchronous as I read somewhere it is not good practice – kittu Jan 02 '17 at 16:02
  • @Satyadev If `entry.phoneNumber` is an empty string, what is expected result? If `getApiResponse` is asynchronous why do you `return base64Image` immediately within `getPhotos` call? What do you expect `retrievedContacts.profileData` value to be? – guest271314 Jan 02 '17 at 16:05
  • @guest271314 then when should I return? – kittu Jan 02 '17 at 16:07
  • Within `getApiResponse`, else `base64Image` can be the original empty string. Does `getApiResponse` return a `Promise`? – guest271314 Jan 02 '17 at 16:09
  • @guest271314 no it is async. There is no promise as of now – kittu Jan 02 '17 at 16:11
  • Does `.timeout(60000)` wait `60000` before making next call? – guest271314 Jan 02 '17 at 16:13
  • @guest271314 what ?? – kittu Jan 02 '17 at 16:14
  • What is purpose of `.timeout(60000)`? – guest271314 Jan 02 '17 at 16:15
  • @guest271314 So you want me to use time out? I am trying to check if `unirest` supports promises – kittu Jan 02 '17 at 16:16
  • What does `.timeout(60000)` do? `.forEach()` does not wait for `.timeout(60000)`. `javascript` at Question does not appear to return a value from `getApiResponse()` call. Though `getPhotos()` call returns the empty string `base64Image`, without waiting for `getApiResponse()` – guest271314 Jan 02 '17 at 16:17
  • @guest271314 it does return and I am able to print it in console. The problem is forEach doesn't wati – kittu Jan 02 '17 at 16:18
  • 1
    Where is a value returned from `getApiResponse`? You are correct, `.forEach()` does not wait for `getPhotos()` return value. You can perform the calls one at a time until there are no further indexes to get values from within `contacts` array. – guest271314 Jan 02 '17 at 16:20
  • @guest271314 `.end(function (res) {` the `res` parameter is the response – kittu Jan 02 '17 at 16:22
  • 2
    @Satyadev See [promise.all inside a forEach loop — everything firing at once](http://stackoverflow.com/questions/37089515/promise-all-inside-a-foreach-loop-everything-firing-at-once/), [NodeJS promise resolution](http://stackoverflow.com/questions/41411733/nodejs-promise-resolution/) – guest271314 Jan 02 '17 at 16:24

1 Answers1

1

There are 2 ways to solve your issue.

1. You make the ajax call NOT-asynchronous

i,e, make your AJAX call to be synchronous. In this approach, you loose the benefits of asynchronous ajax call.

2. Change the logic of your code. (Recommended!)

So the idea is, when to push your retrieved contact info, you just get the position of it in the array, and then pass this position value to the function getting the photo, so that after photo data is retrieved, just set it to correct responding retrieved contact info.

var retrievedContacts = {};
var retrievedContactsArr = [];
contacts.getContacts(function (err, contacts) {
    contacts.forEach(function (entry) {
        if (entry.phoneNumber !== '') {
            retrievedContacts = {
                contact: {
                    "address": {
                        "home": "",
                        "office": ""
                    }
                },
                "profileData": false
            };
            retrievedContactsArr.push(retrievedContacts);
            var retrievedContactsPos = retrievedContactsArr.length - 1;
            getPhotos(entry.photo, req.token, retrievedContactsPos);
        }
    });
});



function getPhotos(url, token, pos){
    var base64Image = '';
    getApiResponse(url+"?access_token="+token,"", [pos, function (res1, pos)      {
        if (res1.error) {
            console.log('Could not fetch google photos......', res1.error);
        } else {
            base64Image = new Buffer.from(res1.body).toString('base64');
            retrievedContactsArr[pos]["profileData"] = base64Image;
        }
    }]);
}

function getApiResponse(url, params, next) {
    unirest.get(url)
    .query(params)
    .timeout(60000)
    .end(function (res) {
        if (next)
            next[1](res, next[0]);
    });
}

if retrievedContactsArr is not global variable, code might be updated like following:

var retrievedContacts = {};
    var retrievedContactsArr = [];
    contacts.getContacts(function (err, contacts) {
        contacts.forEach(function (entry) {
            if (entry.phoneNumber !== '') {
                retrievedContacts = {
                    contact: {
                        "address": {
                            "home": "",
                            "office": ""
                        }
                    },
                    "profileData": false
                };
                retrievedContactsArr.push(retrievedContacts);
                var retrievedContactsPos = retrievedContactsArr.length - 1;
                getPhotos(entry.photo, req.token, retrievedContactsArr, retrievedContactsPos);
            }
        });
    });
    
    
    
    function getPhotos(url, token, retrievedContactsArr, pos){
        var base64Image = '';
        getApiResponse(url+"?access_token="+token,"", [retrievedContactsArr, pos, function (res1, pos)      {
            if (res1.error) {
                console.log('Could not fetch google photos......', res1.error);
            } else {
                base64Image = new Buffer.from(res1.body).toString('base64');
                retrievedContactsArr[pos]["profileData"] = base64Image;
            }
        }]);
    }

    function getApiResponse(url, params, next) {
        unirest.get(url)
        .query(params)
        .timeout(60000)
        .end(function (res) {
            if (next)
                next[2](res, next[0], next[1]);
        });
    }
Community
  • 1
  • 1
Codemole
  • 3,069
  • 5
  • 25
  • 41
  • `retrievedContactsArr` is not accessible in `getPhotos` function. – kittu Jan 03 '17 at 03:55
  • Why is `retrievedContactsArr` not accessible? Is not it defined in global scope? – Codemole Jan 03 '17 at 04:07
  • Yes it is not in global scope. I changed it to this: `function getPhotos(retrievedContactsArr, url, token, pos)` and function call like this: `getPhotos(retrievedContactsArr, entry.photo, req.token, retrievedContactsPos);` but I am not sure is this correct? – kittu Jan 03 '17 at 04:17
  • @Satyadev I have updated the my answer. However, I say, is there any special reason you should not keep `retrievedContactsArr` global variable? – Codemole Jan 03 '17 at 04:26
  • Error: `Cannot set property 'profileData' of undefined` at: `retrievedContactsArr[pos]["profileData"] = base64Image;` – kittu Jan 03 '17 at 05:55
  • I don't mind having global variable. Kept it private just that it is not good practice – kittu Jan 03 '17 at 05:55
  • Its ok there was problem with scope. I fixed it. One more small issue is I am not able to generate image from base64 I have created. I am fetching photos from google contacts and api uses byte array conversion like this: https://developers.google.com/google-apps/contacts/v3/#retrieving_a_contacts_photo – kittu Jan 03 '17 at 06:06
  • @Satyadev Well, I am not much sure about image convert thing.. maybe you can ask another question regarding your issues. Google API experts will not hesitate to help you :D – Codemole Jan 03 '17 at 13:24