Coffeescript变量从在错误范围内声明的promise返回

时间:2018-06-13 12:20:01

标签: for-loop scope promise coffeescript

我对Coffeescript的预期行为有疑问。这是一个错误吗?

通过for循环映射列表的元素并再次将它们取出是没有问题的。

idList = [0, 1, 999]
results =
  for id in idList
    id
console.log results # prints [ 0, 1, 999 ]

现在,让我们再次将它们包含在承诺中。这打破了。

查看代码是最好的解释。

对于for循环,它总是打印999

idList = [0, 1, 999]
operations =
  for id in idList
    new Promise (resolve, reject) -> resolve()
    .then -> id

Promise.all operations
.then (results) ->
  console.log results # prints [ 999, 999, 999 ]

你会认为for循环行(第3行)中的 id 是在该范围内声明的。

通过将第3行更改为使用地图,它的行为符合预期

idList = [0, 1, 999]
operations =
  idList.map (id) ->
    new Promise (resolve, reject) -> resolve()
    .then -> id

Promise.all operations
.then (results) ->
  console.log results # prints [ 0, 1, 999 ]

当从for循环中的promise内部返回变量并检查已编译的Javascript代码时,我们可以看到Coffee编译器已在外部作用域声明了该变量,因此返回的值成为最后一个值变量,总是。

var id, idList, operations;  # id is declared here
idList = [0, 1, 999];
operations = (function() {
   var i, len, results1;
   results1 = [];
  for (i = 0, len = idList.length; i < len; i++) {
    id = idList[i];
    results1.push(new Promise(function(resolve, reject) {
      return resolve();
    }).then(function() {
      return id;
      .......

这是真的有意,是一个错误,还是一个需要注意的警告?

CoffeeScript版本2.1.1 节点版本v9.7.1

1 个答案:

答案 0 :(得分:0)

你的第三个猜测是正确的 - 需要注意的是它需要注意的事项。

这是预期的行为,是Javascript和Coffeescript中常见的错误来源。

变量在循环内的顶部作用域中指定。如果要在循环中定义函数或承诺,它们都将引用相同的变量。如果在循环移动后执行函数/ promise,则变量不会包含您期望的内容。要解决这个问题,请使用Coffeescript中的do运算符,该运算符将编译为Javascript中立即执行的匿名函数。

do示例中使用Promise运算符:

idList = [0, 1, 999]
operations =
  for id in idList
    do (id) ->   
      new Promise (resolve, reject) -> resolve()
      .then -> id

查看loops and comprehensions section

末尾的官方文档
  

当使用JavaScript循环生成函数时,通常会插入一个闭包装,以确保循环变量被关闭,并且所有生成的函数不只是共享最终值。 CoffeeScript提供do关键字,它立即调用传递的函数,转发任何参数。