learnyounode icon indicating copy to clipboard operation
learnyounode copied to clipboard

exercise 9 Juggling Async

Open thekindlyone opened this issue 8 years ago • 6 comments

After trying a lot of different things, this worked, but I don't fully understand why.

var http = require('http')
var bl = require('bl')
var urls=process.argv.slice(2)
var out = []
var done= 0

for (var i = 0; i < urls.length; i++) {
    http.get(urls[i],function(index){
        return function(response){
            response.setEncoding('utf8')
            response.pipe(bl(function(err,data){
                out[index] = data.toString() 
                done+=1   
                if (done==3){
                    for (var i = 0; i < out.length; i++) {
                        console.log(out[i])
                    }
                }
            }))
        }
    }(i))
}

Why do I have to pass i to the higher order function that returns the callback for http.get but not out and done. done will also be changed asynchronously by the callbacks of response.pipe, and so will out. I feel like I am only partly getting the hang of this async functional stuff. HELP!

thekindlyone avatar Sep 05 '16 22:09 thekindlyone

The point of this exercise is to understand that for (var i = 0; i < urls.length; i++) { is a sync control flow tool. The work you are doing however is async.

By async work, I mean that the call to http.get does not block execution. So as you iterate (for loop), the value i will change before the async call finishes.

You avoid this by making a copy of the value:

for (var i = 0; i < 10; i++) {
  (function(index) {
    // index is a copy of "i"
  })(i);
}

You need not copy anything else because the references are not changing while you are iterating.

I struggled with this as well, see #287. My solution is also available as a gist.

More information available from the no-loop-func eslint rule, on mdn, and stack overflow. You should also take the time to learn about Function.bind which can be used to accomplish the same thing.

CodeMan99 avatar Sep 06 '16 21:09 CodeMan99

Thank you for the links. I will go through them now. Will close if I don't have any further questions.

thekindlyone avatar Sep 06 '16 21:09 thekindlyone

Looks like you don't have any further questions :P @thekindlyone

AnshulMalik avatar Aug 29 '17 09:08 AnshulMalik

actually we could use this way to resolve this problem

const http = require('http')
const url = process.argv.slice(2)
let resultQueue = []
let counter = 0

for(let i=0;i<url.length;i++){
    http.get(url[i], function(response){
        let result = ""
        response.setEncoding("utf8")
        response.on("data", function(chunk){
            result += chunk
        })
        response.on("end", function(){
            resultQueue[i] = result
            counter ++
            if(counter == url.length){
                resultQueue.forEach(function(item){
                    console.log(item)
                })
            }
        })
    })
}

I think it's more easy to understand than before

MeloGuo avatar Nov 04 '17 08:11 MeloGuo

@Mluka I agree removing the bl dependency is more clear, but that is not the point of the exercise or question. However, the advantage of let vs var is nice since your http.get callback is in the block scope of the let. The let statement has only recently gained popular usage.

For the sake of defensive coding, you should probably still use an IIFE on diverse teams. You don't want a teammate changing that let to var and have everything break. It could lead to a very difficult to track bug.

CodeMan99 avatar Nov 04 '17 19:11 CodeMan99

@CodeMan99 Thank you for your reminding. I'm still student. Never worked with anyone else.Your words help me a lot!

MeloGuo avatar Nov 05 '17 02:11 MeloGuo