1

Can someone explain to me what is happening to the for loop here? I don't understand why the loop goes beyond its' condition. I expect condition [i] to stop at '2'. I suppose that this behavior would be same for other async functions inside a for loop. Much thanks!

var path = require('path')
var fs = require('fs')
var request = require('request')
var cheerio = require('cheerio')

for (i=0; i < 3; i++) {
    console.log(i)
    var arr = []
    var url = 'https://en.wikipedia.org/wiki/Web_scraping'

    request (url, function(error,response,body) {
      if(error){
        throw err;
      } $ = cheerio.load(body)

      var x = $.html()
      console.log(i)
    })
}

/* Results
0
1
2
3
3
3
*/
SimpleBeat
  • 747
  • 1
  • 10
  • 20
mattK
  • 73
  • 6
  • dry run this condition `for(i=0; i < 3; i++){` if its start from `0` it will run `3` times to run only two times either change `for(i=1; i < 3; i++){` or `for(i=0; i < 2; i++){` or `for(i=0; i <= 1; i++){` – Niklesh Raut Apr 17 '17 at 05:56

2 Answers2

2

Your loop executes exactly 3 times and logs i here - 0,1,2:

for(i=0; i < 3; i++){
    console.log(i)
    ^^^^^^^^^^^^^  <--------------------------------

But then callback that is added here

request(url, function(error,response,body){
             ^^^^^^^^^^^^ <----------------------------

is executed 3 times and logs 3, 3, 3:

    if(error){
        throw err;
    } $ = cheerio.load(body)
        var x = $.html()
        console.log(i)
        ^^^^^^^^^^^^^ <-----------------------------
    })

See this question to understand why i is outputted as 3 in the callback three times, instead of 0, 1, 2 as you'd probably expect. If you used let instead of var, you'd get expected 0, 1, 2:

for(let i=0; i < 3; i++){
Community
  • 1
  • 1
Max Koretskyi
  • 101,079
  • 60
  • 333
  • 488
  • Thanks Maximus for breaking it down. Actually, I don't expect the function to return "0,1,2" because I know that "i" condition already reached its' maximum by the time async returned any value. I would expect "2, 2, 2". If it loop executed 3 times, and not 4 times, then why there is ever a "3" ? Thanks for the link. I'll go through it now! – mattK Apr 17 '17 at 06:41
  • @user7861743, you're welcome. _I would expect "2, 2, 2"_, no, the `i` counter is to `3` when the loop ends... this is why `i < 3` condition evaluates to `false` and loop is over. Otherwise, if `i` stayed `2`, the loop would never end. Btw, you can accept my answer if it helped – Max Koretskyi Apr 17 '17 at 06:49
1

Maximus tells you why the code is doing what it does. But how do you fix it? The most basic way, if you can use ES6 language features (which you probably can, since it looks like you're using Node.js), is to declare your variables with let instead of var. let behaves in a way that you would probably expect. In addition to behaving more reasonably when referenced from lambdas, variables declared with let are block scoped instead of function scoped, which is more intuitive.

Brian McCutchon
  • 8,354
  • 3
  • 33
  • 45
  • Thanks Brian for the explanation. I was using self-executing function to go around that issue I was facing, but I tried 'let' and it seems that the output is exactly the same. – mattK Apr 17 '17 at 07:22