2

I have events flowing to below program, so based event value i trigger different promises so i wanted to check if its good idea to use promises inside forEach.

With below code when i have element.variables.caseIdFound value in event it never get satisfied the condition. Any idea what is implemented wrong i am fairly new to the promises. Any example with below code highly appreciated.

camunda.js

 var caseIdFound;
    var processingCompleted;

    function checkTicketNum(element) {
        var EventCasesID;
        var event;
        var ticketNumber;
        var CasesID;
        var insertIDBEvents = [];
        var event;
        return new Promise(function(resolve, reject) {
              event = JSON.parse(element.variables.event.value);
              ticketNumber = event.body.raw.tkt;
              CasesID = event.body.raw.CasesIDuuid;
                controller.insertCase(ticketNumber, function(err, response) {
                    event.body.raw.LogIDuuid = generateUUID();
                    if (response.length == 0) {
                        completeTask('TicketNotFOund',element.id);
                    } else {
                        EventCasesID = response[0].CasesID;
                        if(CasesID === EventCasesID) {
                            caseIdFound = true;
                            completeTask(element.id,caseIdFound);
                            processingCompleted  = true;
                            resolve(processingCompleted);
                         }

                    }
                })
        });


    }

function postIDBCall(element) {
    var event;
    return new Promise(
        function(resolve, reject) {
             event = JSON.parse(element.variables.event.value);
             controller.insertTicketAndCase2(event.body.raw, function(err, response2) {
              controller.insertTicketAndCase(event.body.raw, function(err, response1) {
                completeTask(event.id);
                console.log("InsertIDB Result Completed",element.id);
                processingCompleted = true;
                 resolve(processingCompleted);
              })
          })

  });
}


    module.exports = {
        checkTicketNum: checkTicketNum,
        generateUUID: generateUUID,
        completeTask: completeTask
    };

promise.js

 var camunda = require('./camunda');

     data.forEach(function(element) {
         if (!element.variables.caseIdFound) {
             camunda.checkTicketNum(element).then(function(processingCompleted) {
                 console.log('1st Box', processingCompleted);
             });
         }else if(element.variables.caseIdFound) {
                      console.log('END BOX IF', element.variables.caseIdFound);
                      camunda.postIDBCall(element).then(function(processingCompleted){
                            console.log('2nd Box', processingCompleted);
                      });
                    }
     });
hussain
  • 6,587
  • 18
  • 79
  • 152
  • 1
    Take a look at [`Promise.all()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) –  Sep 05 '17 at 19:39
  • You should create an array of promises and then use Promise.all() and insert your promise array there. – Rickard Elimää Sep 05 '17 at 19:56
  • 1
    Be aware promise.all will process everything in parallel, depending on what what your promises are doing, this could cause problems. Bluebird has a map with a concurrency option that's worth a look. Also a simple for loop mixed with asyn/await is also an idea. – Keith Sep 05 '17 at 19:59
  • can you please provide me example with above code how i can use promise.all , i am new to promises i will really appreciate the help. – hussain Sep 05 '17 at 20:39
  • You should never use `forEach`. It only causes problems. – Bergi Sep 05 '17 at 21:09

2 Answers2

2

EDIT: Thanks to @Bergi for the comments. There was a mistake, you still need Promise.all().then()

Here is a ES8 version of @serendipity code:

const data = [false, 10, 20, 30]
const { checkTicketNum, postIDBCall } = require("./camunda")
const result = data.map(async(pieceOfData) => {
    return (!pieceOfData) ?
        await checkTicketNum(pieceOfData) :
        await postIDBCall(pieceOfData)
})
Promise.all(result).then(x => { console.log(x) })

With some comments :

    //fake data
const data = [false, 10, 20, 30]
    //destructuring to avoid camunda.functionName
const { checkTicketNum, postIDBCall } = require("./camunda")
    //map will return a table containing the result of your promises
    //Logging within a foreach does not garuantee you that the order of the logs is the execution order. Therefore, you can log at the end.
const result = data.map(async(pieceOfData) => {
        //if(a){myVar=1} else if(!a){myVar=2} is not a good programing syntax. 
        //Consider if(a){myVar=1} else {myVar=2}, 
        //and even the ternary operator myVar = (a)?{1}:{2}
        //here : 
        return (!pieceOfData) ?
            await checkTicketNum(pieceOfData) :
            await postIDBCall(pieceOfData)
    })
    //finally lof the result. JSON.stringify will help with nested JSON
Promise.all(result).then(x => { console.log(x) })

If you want to test, here is a fake camun.js file:

checkTicketNum = (x) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("This is the checkTicketNum for " + x)
        }, 1000)
    })
}

postIDBCall = (x) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(x + 1)
        }, 1000)
    })
}

module.exports = {
    checkTicketNum,
    postIDBCall
}

EDIT: Well, to ensure what @Bergi told me, I wrote a whole fake library, so here is a working example. I was also curious about performance issues so I tested and the execution time is exactly the same with both async/await or promises.

//CAMUN.JS
checkTicketNum = (x) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("This is the checkTicketNum for " + x)
        }, 1000)
    })
}

postIDBCall = (x) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(x + 1)
        }, 1000)
    })
}

//module.exports = {
//    checkTicketNum,
//    postIDBCall}



//--------------------------//





//MAIN FILE

//destructuring to avoid camunda.functionName
//const { checkTicketNum, postIDBCall } = require("./camunda")


//fake data
const data = [false, 10, 20, 30]

console.time("await")
const resultAwait = data.map(async(pieceOfData) => {
    return (!pieceOfData) ?
        await checkTicketNum(pieceOfData) :
        await postIDBCall(pieceOfData)
})
Promise.all(resultAwait).then(x => {
    console.timeEnd("await")
    console.log("Await result : " + JSON.stringify(x))
})


console.time("promiseAll")
const resultPromises = []
data.map((pieceOfData) => {
    return (!pieceOfData) ?
        resultPromises.push(checkTicketNum(pieceOfData)) :
        resultPromises.push(postIDBCall(pieceOfData))
})
Promise.all(resultPromises).then(x => {
    console.timeEnd("promiseAll")
    console.log("Promise result : " + JSON.stringify(x))
})
Théophile Pace
  • 826
  • 5
  • 14
  • I think you forgot the `await Promise.all` somewhere? – Bergi Sep 06 '17 at 04:36
  • The point of **await** is that the table is filled with the result of the Promises, not the promises themselves. The table will be filled once the promise returns. The idea of **await** is to allow you to fake synchronous code in Javascript. – Théophile Pace Sep 06 '17 at 04:39
  • Well, no. The `result` table is filled with promises in your code. – Bergi Sep 06 '17 at 04:48
  • Indeed! Thanks for the comment, I was not expecting the system to behave like this ... I wrote a snippet and edited the post in consequence. – Théophile Pace Sep 06 '17 at 05:17
0

This is how I like to use promise in a loop

let data = [10,20,30];
let promises = [];

data.forEach(function (eachData) {
    let promise = new Promise((resolve,reject) => {
        setTimeout(function () {
            let newData = eachData + 10;
            resolve(newData)
        }, 1000)
    });

    promises.push(promise);
});

Promise.all(promises).then((data) => {
    console.log(data)    //returns [20,30,40]
});

Basically what happens is that for each promise that you run in a loop, you push that promise into an array and inside the argument of Promise.all() you inject the promises array. It can then be used as a promise it self so .then() function is possible. The data is returned in an order of injection into the promises array not in order of resolve()

forJ
  • 4,309
  • 6
  • 34
  • 60