javascript - 杂耍异步 - LearnYouNode - 立即调用函数表达式

标签 javascript node.js asynchronous closures

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

我尝试过的一些事情是:

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

具体以asyncCounter为索引,将返回的数据放在存储数组中。因此,当事件“结束”发生时,我会将其设置为您在下面的代码中看到的那样。显然这是行不通的,因为调用是在不同的时间完成的。

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

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();

所以我再次点击谷歌,因为我不知道他们在做什么,我发现了一种叫做闭包的东西。根据我的理解,他们所做的让我处于回调的范围内 - 我不知道如何做到这一点,认为这是不可能的并很早就放弃了。

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

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

预先感谢您的宝贵时间!

史蒂夫。

最佳答案

这称为立即调用函数表达式 (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 或更高版本中是可能的。这引入了 block 作用域变量。您可以阅读更多相关信息here 。例如:

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

关于javascript - 杂耍异步 - LearnYouNode - 立即调用函数表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26147077/

相关文章:

javascript - Highcharts.js 看不到图表线

node.js - 无法通过终端启动nodeapp

php - 使用 proc_open 流式传输运行时生成的 gzip 文件

c# - 在等待 List<Task> 中的所有任务完成时显示进度

c# - 异步等待 Task.Run 与 HttpClient.GetAsync

javascript - 短时间后更改回图像源

php - MongoDB 更新嵌套数组

javascript - 如何使用 AngularJs 从文本框中检索值?

node.js - 无法使用 node npm 安装任何软件包

javascript - 自动更新package.json中的依赖项