【问题标题】:Juggling Async - LearnYouNode - Immediate Invoked Function Expression杂耍异步 - LearnYouNode - 立即调用函数表达式
【发布时间】:2014-11-26 15:08:21
【问题描述】:

我一直在 learnyounode 的帮助下学习 nodejs,但我被困在 Juggling Async 上。我的解决方案适用于问题中最困难的部分——它会乱序打印结果,因为某些请求先于其他请求完成。我试图在不使用任何帮助库的情况下做到这一点,所以我点击了谷歌。我找到了一个关于控制流的很棒的教程 - http://book.mixu.net/node/ch7.html 但发现他的系列解决方案 - 一个异步 for 循环只能正常工作,因为他的异步函数在返回之前正好等待 1000 毫秒。在我的示例中,时间是可变的,因此您不会以相同的顺序获得结果。然后该示例开始构建一个控制流库,我觉得虽然它可以工作,但它超出了 learnyounode 分配的范围。

我尝试过的一些事情是:

创建一个包含 URL、订单和响应数据的对象。我遇到的问题是,当我坐在 http.get 的回调中时,我不知道我正在获取数据的 URL。这实际上是我尝试过的大多数事情的错误,我不知道如何将我得到的数据链接到回调中的特定 URL。

使用 asyncCounter 作为索引在存储数组中专门设置一个位置 foe 返回的数据。因此,当事件“结束”发生时,我会按照您在下面的代码中看到的那样设置它。显然这不起作用,因为呼叫在不同的时间结束。

最终,我屈服并研究了解决方案。这是我所拥有的,我用星号标记了我的解决方案中缺少的内容。

var http = require("http");

var storage = [];
var urlList = [];
var asyncCounter = 0;

//Store all the URL's from the command line in an array
for(var i = 2; i < process.argv.length; i++){
    urlList.push(process.argv[i]);
}

//This function prints out all the data in the storage array
//The storage array contains the data received from http.get
function AsyncComplete(){
    for(var j = 0; j < storage.length; j++){
        console.log(storage[j]);
    };
};

//Entry function
function main(){
    //Do an http get on each of the provided URL's
    for(var i = 0; i < urlList.length; i++){
        **(function(i){**
            http.get(urlList[i], function(response){
                var body = "";
                //Got a chunk of data!
                response.on("data", function(chunk){
                    body += chunk;
                });
                //All done with our request. 
                response.on("end", function (){
                    //Store the total response from the URL
                    storage[**i**] = body;
                    asyncCounter++;
                    //Check if we can print the results yet
                    //I want to wait for all the calls to complete so I am using a counter
                    if(asyncCounter == urlList.length){
                        AsyncComplete();
                    }
                });
            });
        **})(i);**
    };
};

main();

所以我再次访问 Google,因为我不知道他们在做什么,我发现了一种叫做闭包的东西。根据我的理解,他们所做的让 i 在回调的范围内 - 我无法弄清楚如何做到这一点,认为这是不可能的并且很早就被放弃了。

我的问题是这究竟是如何工作的——特别是为什么他们有 (i) 他们在哪里工作以及它的用途是什么。没有那个 (i),解决方案就无法工作。为什么你可以把 (function(i){ 扔到某个地方?这是我一百万年来从未想过要做的事情。

我找到了https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures,它似乎可以包含答案,所以我将在此期间通读它,看看我是否能想出一些东西。

提前感谢您的宝贵时间!

史蒂夫。

【问题讨论】:

    标签: javascript node.js asynchronous closures


    【解决方案1】:

    这称为立即调用函数表达式 (IIFE),它只是一个立即调用的匿名函数。传递给此 IIFE 的参数 i 被放入调用堆栈,并且与 for 循环的外部 i 不同。你可以把它想象成创建当前 i 的快照。

    在 JavaScript 中,用“var”声明的变量是函数范围的。因此,所有回调都将访问相同的 i (这不是您想要的)。通过将它传递给 IIFE(这是一个函数,因此为变量 i 引入了自己的范围),为每个回调创建了一个新变量。 如果你给 IIFE 的参数取另一个名字,它会变得更加明显。例如:

    (function(savedI) { // ... })(i)

    通过命名参数i,原来的i被遮蔽了。这意味着您无法访问 IIFE 内部的“外部” i。

    使用 IIFE 的常见替代方法是使用 map。例如:

    _.range(urlList.length).map(function(i) { // ... })

    _.range 是来自underscore 的实用函数,它给出了一个从 0 到 urlList.length 的数组。传递的函数将接收一个不会被覆盖的参数 i(就像你的版本中的 for 循环一样)。

    另一种避免该问题的方法是通过 let 声明变量,这在 EcmaScript 6 或更高版本中是可能的。这引入了块范围的变量。你可以阅读更多关于它的信息here。例如:

    for(let i = 0; i < urlList.length; i++){ // .. }

    【讨论】:

    • 非常感谢您的评论。完美回答我的问题。我在看完全错误的东西。 -.-
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-28
    • 2017-04-06
    • 1970-01-01
    • 2017-02-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多