Async: 异步不会在函数运行时防止数组修改

创建于 2014-07-04  ·  6评论  ·  资料来源: caolan/async

当您有异步调用时,例如。 async.each ,并且在该运行时,传入的数组对其进行了修改,然后它可能永远不会完成,或者将调用两次回调。

例子:

async = require "async" 
arr = [1, 2, 3]  
async.each arr, ((i, cb) -> console.log "i"; setImmediate(cb)), (err) -> console.log "done" 
arr.push(4)

此示例循环遍历 3 个原始数组元素,并打印i但从不调用回调,因为在async.each它正在执行:

if (completed >= arr.length) {
  callback(null);
}

在查看异步代码时,它正在与arr.length进行比较,这可能会改变......存储原始数组长度并与之进行比较是否更好,确保将调用完成的回调?

小提琴:
http://jsfiddle.net/4ysKX/1/

最有用的评论

事后异步最终是否禁止数组修改? 我有一个用例,其中最好在事后修改原始数组,以便迭代比最初更多的元素。

所有6条评论

你是对的——所以不要修改它。 如果您稍后需要更改它,请在将其传递给异步函数之前克隆您的数组。

@aearly是的,这是一种解决方法,但这里的潜在问题是,这是一个很容易犯的错误,并且是一个很难调试的问题,因此恕我直言,它应该在核心库中修复。 这让我在两个不同的项目上咬了两次。

在不克隆输入的情况下很难防止 _any_ 数组修改,例如迭代器函数中的同步修改,但是即使没有克隆,防止与迭代无关的 _asynchronous_ 修改也应该很简单——实际上只是不假设arr.length将在启动异步作业后保持不变(在迭代之前将长度保存在变量中)。

(我与@bradens 合作——如果欢迎的话,我们中的一个人将提交带有测试的 PR)。

欢迎使用带有测试的 PR,但最终要由@caolan来合并它。

这样做的缺点是数组复制的额外开销。 如果你有一个大数组,或者多次调用async.each等,它会变慢并使用更多的内存。

我同意数组副本的开销太大。 我建议由于数组的初始迭代是同步的,因此可以保存原始数组长度以供以后检查。 这样,回调调用的次数将与迭代器调用的次数相匹配,即使在此期间更改了数组。 对于async.each ,这应该很容易并且零开销,但我还没有研究其他函数。 如果所有并行函数在这些情况下都具有一致的行为,那就太好了。

这是#557 的副本。

事后异步最终是否禁止数组修改? 我有一个用例,其中最好在事后修改原始数组,以便迭代比最初更多的元素。

此页面是否有帮助?
0 / 5 - 0 等级