Rakesh K
Rakesh K

Reputation: 51

Create promises on queries inside a for loop

I'm trying to write a Node.js code that does the below.

  1. Connect to a Salesforce instance.
  2. Get the past 7 days, and loop through them.
  3. Run 2 queries inside them and push the result to an Array.
  4. Display the value in another function.

Here is my JS code.

var jsforce = require("jsforce");
var moment = require('moment');

function connectToEP() {
    var main_Obj = {};
    var response_Obj = {};
    var pastSevenDaysArray = [];
    var conn = new jsforce.Connection();
    var beforeSevenDays = moment().subtract(7, 'days').format('YYYY-MM-DD');
    var today = moment().startOf('day');
    var i = 0;


    conn.login("myUid", "myPwd").then(() => {
        console.log("Connected To Dashboard");
        for (var m = moment(beforeSevenDays); m.diff(today, 'days') <= 0; m.add(1, 'days')) {
            conn.query("SELECT SUM(Total_ETA_of_all_tasks__c), SUM(Total_ETA__C) from Daily_Update__c where DAY_ONLY(createddate)= " + m.format('YYYY-MM-DD')).then(() => {
                console.log("B1");
                var z = response_Obj.aggrRes;
                response_Obj.aggrRes = res;
                pastSevenDaysArray.push({ z: res });
                console.log("B1 Exit");

            }).then(() => {
                conn.query("SELECT count(Id), Task_Type__c FROM Daily_Task__c where DAY_ONLY(createddate) = " + m.format('YYYY-MM-DD') + " group by Task_Type__c").then(() => {
                    console.log("B2");

                    var z = response_Obj.aggrRes;
                    response_Obj.aggrRes = res;
                    pastSevenDaysArray.push({ z: res });
                    console.log("B2 Exit");

                })
            })
        }
        return Promise.resolve(pastSevenDaysArray);
    }).then((data) => {
        console.log(typeof data);
        updateMessage(JSON.stringify(data));
        console.log(typeof data);
    });

}
function updateMessage(message) {
    console.log("XXXXXXXXXXXX");
    console.log(message);
    console.log("XXXXXXXXXXXX");
}
function socketNotificationReceived() {
    console.log("socket salesforce rec");
    connectToEP();
}

socketNotificationReceived();

when I run this code, the output that I get is.

socket salesforce rec
Connected To Dashboard
object
XXXXXXXXXXXX
[]
XXXXXXXXXXXX
object
B1
B1
B1
B1
B1
B1
B1
B1

I'm very new to this js platform, unable to get the promises concepts :(. please let me know on were am I going wrong and how can I fix it.

An explanation of what's going is very helpful in my future projects.

Thanks

Upvotes: 1

Views: 366

Answers (2)

danh
danh

Reputation: 62676

The thing I always do when I get confused is to decompose. Build the pieces one by one, and make sure each works. Trying to understand your code, I get something like this...

A function each for logging in, getting a "task sum" from the db and getting a "task count" from the db. (Task sum/count is what I guessed the queries were up to. Rename as you see fit).

var jsforce = require("jsforce");
var moment = require('moment');

function login(conn) {
    return conn.login("myUid", "myPwd");
}

function queryTaskSumForDay(conn, m) {
    return conn.query("SELECT SUM(Total_ETA_of_all_tasks__c), SUM(Total_ETA__C) from Daily_Update__c where DAY_ONLY(createddate)= " + m.format('YYYY-MM-DD'));
}

function queryTaskCountForDay(conn, m) {
    return conn.query("SELECT count(Id), Task_Type__c FROM Daily_Task__c where DAY_ONLY(createddate) = " + m.format('YYYY-MM-DD') + " group by Task_Type__c");
}

With those working, it should be easy to get a sum and a count for a given day. Rather than returning these in an array (containing two objects that each have a "z" property as your code did), I opted for the simpler single object that has a sum and count property. You may need to change this to suit your design. Notice the use of Promise.all() to resolve two promises together...

function sumAndCountForDay(conn, m) {
    let sum = queryTaskSumForDay(conn, m);
    let count = queryTaskCountForDay(conn, m);
    return Promise.all([sum, count]).then(results => {
        return { sum: results[0], count: results[1] };
    });
}

With that working, it should be easy to get an array of sum-count objects for a period of seven days using your moment logic and the Promise.all() idea...

function sumAndCountForPriorWeek(conn) {
    let promises = [];
    let beforeSevenDays = moment().subtract(7, 'days').format('YYYY-MM-DD');
    let today = moment().startOf('day');
    for (let m = moment(beforeSevenDays); m.diff(today, 'days') <= 0; m.add(1, 'days')) {
        promises.push(sumAndCountForDay(conn, m));
    }
    return Promise.all(promises);
}

With that working (notice the pattern here?), your OP function is tiny and nearly fully tested because we tested all of it's parts...

function connectToEP() {
    let conn = new jsforce.Connection();
    return login(conn).then(() => {
        return sumAndCountForPriorWeek(conn)
    }).then(result => {
        console.log(JSON.stringify(result));
        return result;
    }).catch(error => {
        console.log('error: ' + JSON.stringify(error));
        return error;
    });
}

Upvotes: 1

Cody Geisler
Cody Geisler

Reputation: 8617

I think your general structure should be something like this. The biggest issue is not returning promises when you need to. A "for loop" of promises is a little difficult to step into, but if you can do them in parallel then the easiest thing to do is Promise.all If you need to aggregate the data before you can perform the next query then you need to do multiple Promise.all().then()'s. The reason you get an empty array [] is because your for loop creates the promises but doesn't wait until they finish.

var jsforce = require("jsforce");
var moment = require('moment');

function connectToEP() {
  // connectToEP now returns a promise  
  return conn.login("myUid", "myPwd").then(() => {
        console.log("Connected To Dashboard");
        let myQueries = [];
        for (start ; condition ; incrementer) {
            myQueries.push( // Add all these query promises to the parallel queue
                conn.query(someQuery)
                    .then((res) => {
                        return res;
                    })
                    .then((res) => {
                        return conn.query(someQuery).then((res) => {
                            return someData;
                        })
                    })
            )
        }
        return Promise.all(myQueries); // Waits for all queries to finish...
    }).then((allData) => { // allData is an array of all the promise results
        return updateMessage(JSON.stringify(allData));
    });
}

Upvotes: 0

Related Questions