通过let关于变量范围与声明变量的疑问

时间:2018-01-09 18:26:25

标签: javascript asynchronous ecmascript-6

在我的观点中,我在一本书中遇到了奇怪的断言。我想我不明白,但无论如何,如果你了解情况会很好。

ajax('<host1>/items',                                       
   items => {
   for (let item of items) {
      ajax(`<host2>/items/${item.getId()}/info`,            
      dataInfo => {
        ajax(`<host3>/files/${dataInfo.files}`,            
           processFiles);
   });
  }
});

作者注意:

  

此代码还有另一个隐藏的问题。你能猜出这是什么?当您混合一个同步工件(如for..of命令块)调用异步函数时,就会发生这种情况。循环并不知道这些调用存在延迟,因此无论如何它们总是会前进,这可能会导致一些真正无法预测且难以诊断的错误。在这些情况下,您可以通过使用forEach()而不是循环来管理异步函数的闭包来改善问题。

而不是它们提供以下内容:

ajax('/data',                            
  items  => {                            
     items.forEach(item => {
    // process each item             
 });

});

坦率地说,我希望如果我们使用let for循环,这意味着我们为每次迭代创建一个闭包,因此我没有看到任何隐藏的问题。

2 个答案:

答案 0 :(得分:2)

你是对的,如果作者的评论是在那个确切的代码片段上,那就错了。

  

循环不知道这些调用有延迟[...]你可以通过使用forEach()来改善问题

没有任何改变,forEach()同样不知道在其回调中进行的异步调用,因为for循环是在其体内进行的异步调用。 forEach()将始终向前迈进#34;与for循环相同的方式。

使用let,您无法遇到作者似乎担心的问题,因为循环的每次迭代都有自己的item,就像使用items.forEach( item => { ...时一样。

即使使用var,该代码也没有问题,因为变量item未在ajax请求的回调中使用。您可以使用var并在回调中使用item来产生作者的关注,例如:console.log( item.getId() );

注意:重要的是要注意回调最有可能以不同的(看似随机的)顺序运行。如果你不知道它会导致令人惊讶的错误,但是也与使用循环与forEach无关。

答案 1 :(得分:1)

这本书的作者似乎毫无头绪。 for (let … of …)可以修复.forEach(…)没有问题。

他们谈论

  

围绕异步函数创建闭包,使用forEach()而不是循环

进行管理

但闭包不是由forEach回调函数创建的,闭包是传递给ajax函数的回调。它关闭在周围的范围内,for块范围(使用letconst时)与函数体范围之间几乎没有任何区别(使用forEach时。