learnyounode
learnyounode copied to clipboard
exercise 9 Juggling Async
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!
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.
Thank you for the links. I will go through them now. Will close if I don't have any further questions.
Looks like you don't have any further questions :P @thekindlyone
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
@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 Thank you for your reminding. I'm still student. Never worked with anyone else.Your words help me a lot!