6

I am trying to call a function inside a for loop and the problem is that the function is called after the loop was finished.

Taking the below as an example, it prints to the console:
here1
here1
here2
here2

Instead of
here1
here2
here1
here2

report.forEach(item => {
  item.runs.forEach(run => {
    waComplianceBusiness(req, run.id, (err, res) => {
      const compliance = res.data.overviews[0].compliance;
      var failureList = [];

      compliance.forEach((rule, index) => {
        console.log('here1');
        waRuleOverview(req, run.id, rule.id, (err, res) => {
          console.log('here2');
          // handle the response
        });
      });
    });
  });
});

How can I fix this?

Please let me know if I need to provide additional information


Here is the complete code:

export default (req, callback) => {
  const report = req.body.webAudits;

  if(report.length > 0) {
    report.forEach(item => {
      item.runs.forEach(run => {
        waComplianceBusiness(req, run.id, (err, res) => {
          const compliance = res.data.overviews[0].compliance;
          if(compliance) {
            var failureList = [];
            compliance.forEach((rule, index) => {
              if(rule.pagesFailed > 0) {
                waRuleOverview(req, run.id, rule.id, (err, res) => {
                  const failedConditions = res.data.failedConditions;
                  const ruleName = res.data.ruleName;

                  failedConditions.forEach((condition, failedIndex) => {
                    const request = {
                      itemId: condition.conditionResult.id,
                      itemType: condition.conditionResult.idType,
                      parentId: condition.conditionResult.parentId,
                      parentType: condition.conditionResult.parentType
                    }
                    const body = {
                      runId: run.id,
                      ruleId: rule.id,
                      payload: request
                    }

                    waConditionOverview(req, body, (err, res) => {
                      const description = res.data.description;
                      const conditionValues = res.data.conditionValues[0];
                      var actualValue = conditionValues.value;

                      if(actualValue == "") {
                        actualValue = 'empty';
                      }

                      if(description.idType == "variable") {
                        var failureObj = {
                          ruleName: ruleName,
                          expected: description.name + ' ' + description.matcher + ' ' + description.expected[0],
                          actual: description.name + ' ' + description.matcher + ' ' + actualValue
                        };
                      }
                      else if(description.idType == "tag") {
                        var failureObj = {
                          ruleName: ruleName,
                          expected: description.name + '\n' + description.matcher,
                          actual: actualValue
                        };
                      }
                      failureList.push(failureObj);
                    });
                  });
                });
              }
              if(key + 1 == compliance.length) {
                console.log(failureList);
              }
            });
          }
        });
      });
    });
  }
}

These are the callback functions:

export function waComplianceBusiness(req, runId, callback) {
  const apiToken = req.currentUser.apiToken;
  const payload = {
    'Authorization': 'api_key ' + apiToken
  }

  const options = {
    'method': 'get',
    'gzip': true,
    'headers': payload,
    'content-type': 'application/json',
    'json': true,
    'url': 'api_url'
  }

  request(options, (error, response, body) => {
    callback(null, body);
  });
}

export function waRuleOverview(req, runId, ruleId, callback) {
  const apiToken = req.currentUser.apiToken;
  const payload = {
    'Authorization': 'api_key ' + apiToken
  }

  const options = {
    'method': 'get',
    'gzip': true,
    'headers': payload,
    'content-type': 'application/json',
    'json': true,
    'url': 'api_url'
  }

  request(options, (error, response, body) => {
    callback(null, body);
  });
}

export function waConditionOverview(req, body, callback) {
  const apiToken = req.currentUser.apiToken;
  const payload = {
    'Authorization': 'api_key ' + apiToken
  }

  const options = {
    'method': 'post',
    'gzip': true,
    'headers': payload,
    'body': body.payload,
    'content-type': 'application/json',
    'json': true,
    'url': 'api_url'
  }

  request(options, (error, response, body) => {
    callback(null, body);
  });
}

My goal is to return the failureList array after the loop over the compliance array is done

I found a similar question here but not sure if that would work in my case and I don't really know how to implement the promises

6
  • 4
    This is happening because your inner call (where here2 is printed) runs asynchronously. This means that first all passes of here1 are executed, each scheduling a future execution of here2, and then all the scheduled executions occur. This is what you're seeing. Commented Aug 3, 2017 at 13:30
  • I think this is happening because you're expecting your program to execute synchronously whereas callbacks work asynchronously. Take a look at async/await if you want it to wait for the methods to go in the order that you write them. Commented Aug 3, 2017 at 13:30
  • @slezica I'm a newbie to node.js and an example based on my code would help me a lot.. Commented Aug 3, 2017 at 13:34
  • I can't describe the solution (and why it works, the most important part) briefly in a comment, and I don't have the time to write it out now. It's not a quick fix. I upvoted the question so others will look at it. I can tell you this though: you can approach this problem using libraries such as async as shown here Commented Aug 3, 2017 at 13:37
  • @C0dekid that doesn't solve it Commented Aug 3, 2017 at 13:38

2 Answers 2

-1

The for loop executes the statements inside the scope sequentially. But it does not wait for the the function calls to complete, it continues with the next statement(i.e works asynchronously). That is why the result is as such. You can make it work synchronously using Promises or by using the async module.

As it is not clear what you are going to perform in the function call and what you want the statements to do, I am not able to suggest either of which. . asyn.each is usually preferred for making the for loop execute synchronously. And promises are used when you want to wait for the function to finish executing and then perform operation. You might want to look at their documentation

Promises|MDN

async.each

Thank you, Ragul

Sign up to request clarification or add additional context in comments.

Comments

-1

If you want to do it in sequence use async.eachOfSeries

   async.eachOfSeries(report, function(item, index, eachOfCallback1){

       async.eachOfSeries(item.runs, function(run, index, eachOfCallback2){

          waComplianceBusiness(req, run.id, (err, res) => {

              var failureList = [];
              async.eachOfSeries(compliance, function(rule, index, eachOfCallback3){

                 console.log('here1');
                 waRuleOverview(req, run.id, rule.id, (err, res) => {
                     console.log('here2');
                     return eachOfCallback3(err);
                 });

               }, function(err){
                 if(err)
                    return eachOfCallback2(err);
                 else return eachOfCallback2();
               });

           });
        }, function(err){
           if(err)
              return eachOfCallback1(err);
           else return eachOfCallback1();
        })
    }, function(err){
       // handle final response  
    })

If you want to optimise the process take a look at async.parallel

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.