LearnYouNode - Jugglling Async(#9) - 我一定是在遗漏某些东西

时间:2016-10-13 22:23:35

标签: javascript arrays node.js

这里似乎有很多关于这个问题的问题,但没有一个与我的问题AFAICT直接相关。这是问题陈述:

  

此问题与上一个问题(HTTP COLLECT)相同,因为您需要使用http.get()。但是,这次您将获得三个URL作为前三个命令行参数。

     

您必须收集每个网址提供给您的完整内容,然后将其打印到控制台(标准输出)。您不需要打印长度,只需将数据打印为String;每个网址一行。问题在于您必须按照与作为命令行参数提供的URL相同的顺序打印它们。

这是我失败的原始解决方案:

var http = require('http')
var concat = require('concat-stream')

var args = process.argv.slice(2, 5)
var args_len = args.length
var results = []

args.forEach(function(arg, i) {
    http.get(arg, function(res) {
        res.setEncoding('utf8')
        res.pipe(concat(function(str) {
            results[i] = str
            if (results.length === args_len)
                results.forEach(function(val) {
                    console.log(val)
                })
        }))
    }).on('error', console.error)
})

这是他们推荐的解决方案:

var http = require('http')
var bl = require('bl')
var results = []
var count = 0

function printResults () {
  for (var i = 0; i < 3; i++)
    console.log(results[i])
}

function httpGet (index) {
  http.get(process.argv[2 + index], function (response) {
    response.pipe(bl(function (err, data) {
      if (err)
        return console.error(err)

      results[index] = data.toString()
      count++

      if (count == 3)
        printResults()
    }))
  })
}

for (var i = 0; i < 3; i++)
  httpGet(i)

我未能理解的是我的代码与官方解决方案之间的根本区别。在将回复填充到数组中以便稍后引用时,我的解决方案与其解决方案相同。当我比较两个数组的长度(一个长度增加每个回调的数组)时,它们使用计数器来计算回调的数量;那有关系吗?当我在learnyounode程序的范围之外尝试我的解决方案时,似乎工作正常。但我知道这可能意味着很少......所以有人比我更了解节点......关心解释我哪里出错了? TIA。

2 个答案:

答案 0 :(得分:0)

  

当我比较两个数组的长度(一个长度增加每个回调的数组)时,他们使用计数器来计算回调的数量;这有关系吗?

是的,这很重要。 .length of an array取决于数组中的最高索引,而不是实际分配的元素数。

仅当异步请求的结果返回乱序时才会出现差异。如果您首先分配索引0,然后分配1,然后分配2等等,则.length会匹配已分配元素的数量,并且与{{1}相同}}呃。但现在试试这个:

count

如果您在每次分配后测试长度并在获得var results = [] console.log(results.length) // 0 - as expected results[1] = "lo "; console.log(results.length) // 2 - sic! results[0] = "Hel"; console.log(results.length) // 2 - didn't change! results[3] = "ld!"; console.log(results.length) // 4 results[2] = "Wor"; console.log(results.length) // 4 时输出数组,则会打印

4

答案 1 :(得分:0)

所以事实证明这里有两个不同的问题,其中一个是由@Bergi在上面指出的。这两个问题如下:

  • .length方法实际上并不返回数组中的元素数。而是返回可用的最高索引。这看起来很傻。感谢@Bergi指出这一点。
  • i变量的范围不合适,因此i的值可能会发生变化。当结果回来时,这会导致竞争状态。

我的最终解决方案最终如下:

var http = require('http')
var concat = require('concat-stream')

var args = process.argv.slice(2, 5)
var args_len = args.length
var results = []
var count = 0

function get_url_save(url, idx) {
    http.get(url, function(res) {
        res.setEncoding('utf8')
        res.pipe(concat(function(str) {
            results[idx] = str
            if (++count === args_len)
                results.forEach(function(val) {
                    console.log(val)
                })
        }))
    }).on('error', console.error)
}

args.forEach(function(arg, i) {
    get_url_save(arg, i)
})

将outtermost forEach分解为方法调用可以解决更改i问题,因为i作为参数逐个传入,因此永远不会更改。计数器的添加解决了@Bergi描述的问题,因为.length方法并不像人们想象的那样直观。