如果我遗漏了什么,我很抱歉 - 我还在学习Node的逻辑(尤其是异步工作)。
我想下载一个zip文件,然后保存并解压缩。它看起来像这样:
request(URL)
.pipe(fs.createWriteStream(/path/to/filename))
.on('close', function () {
// I want to unzip and I need access to filename
});
现在的问题是,如果我事先设置了filename
变量,因为它在循环中,到文件下载时,filename
将具有最后一次循环迭代的值。
当然,我发现我们可以通过定义一个可以完成所有这些工作的函数来完成它,比如fecthAndUnzip(URL,path)
。但我的问题是与设计相关:它是我们在Node中做到这一点的唯一方法,还是有更好的方法?例如,我可以在on('close')
事件中访问文件名,如果是,我应该吗?谢谢!
定义一个单独的函数绝对不是唯一的方法,而且有一个更好的方法。
您没有显示完整的代码,但我认为由于您使用var
定义变量的行为。和var filename = 'something'
一样。执行此操作时,您将在global [function]范围中定义filename变量。这意味着无论函数在何处定义,它都可用于函数的每个部分。在您的情况下,即使您在循环中定义变量,它也在整个函数中作用域,因此您实际上每次迭代都会覆盖该变量。
如果你想了解更多或者如果我没有足够好地解释它,那么有关于范围的一个很好的stackoverflow答案:https://stackoverflow.com/a/11444416/6359249
解决方案是在当前块范围中定义变量。如果您使用的是实现ES6的较新版本的node.js,则可以使用let
(或const
作为常量变量)。与var
不同,这将在当前块中定义一个函数。如果在循环中定义它,则只能访问该块,并且不会更改。例如:
for (let i = 0; i < files.length; i++) {
let filename = files[i];
// Do stuff
}
注意let i = 0
。相同的范围概念适用于循环变量。这是人们使用var i = 0
并在使用异步函数时发现混淆并发现i
是i
的最后一个值的常见问题。
如果您不使用ES6,则可以在每个循环中定义匿名函数:
for (var i = 0; i < files.length; i++) {
var filename = files[i];
(function (file) {
// Do stuff
})(filename);
}
这与您描述的解决方案相同,只是您正在动态创建和执行该功能。如果由于某种原因没有使用ES6,那么创建单独函数的原始答案可能是最佳解决方案。
不知道它是否对您有所帮助,但您在for循环中描述的问题有多种解决方案:
第一个,使用let而不是var将变量绑定到它的当前范围:
for (let i = 0; i < 4; i++) {
setTimeout(function() {
console.log('test 2', i);
}, 2000);
}
这将打印0,1,2,3,即使循环将在第一个超时回调被调用之前结束。
如果它是一个数组,你可以使用forEach而不是for循环
[0, 1, 2, 3].forEach(function(i) {
setTimeout(function() {
console.log('test 3', i);
}, 3000);
})
你可以使用外部功能:
for (var i = 0; i < 4; i++) {
setTimeout(function() {
logI(i);
}, 100);
}
function logI(i) {
console.log(i);
}
唯一重要的是你的变量需要绑定到内部作用域以保持它的值,即使循环在你的异步函数被调用之前结束。