我正在编写一个函数,使用请求包批量下载图像。 下载部分有效,但我在将参数传递到 Promise 数组时遇到问题。
function downloadImages(data) {
var promises = [];
var promise, local, id, url;
for (var i in data) {
(function(i) {
local = "public/images/".concat(data[i].id, ".png");
url = data[i].img_url
id = data[i].id
promise = request.get({
url: url,
local: local,
id: id,
encoding: 'binary'
}, function(err, res) {
if (!err && res.statusCode === 200) {
fs.writeFile(local, res.body, {
encoding: 'binary'
}, (err) => {
if (!err) {
doSomething()
} else {
console.log("Error Downloading image")
}
})
}
})
promises.push(promise)
})(i)
}
Promise.all(promises);
}
当我运行它时,数组中的所有参数都解析为最后一个条目,因此它被下载 (data.length) 次。
我尝试了一些方法,但无法更接近解决方案。我做错了什么根本性的事情还是相当简单的事情?非常感谢您的帮助!
最佳答案
我建议像这样简化:
// make promisified version of fs.writeFile()
fs.writeFileAsync = function(fname, data, options) {
return new Promise((resolve, reject) => {
fs.writeFile(fname, data, options, err => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
function downloadImages(data) {
let promises = data.map(obj => {
let local = "public/images/".concat(obj.id, ".png");
let url = obj.img_url;
let id = obj.id;
return request.get({url, local, id, encoding: 'binary'}).then(imgData => {
return fs.writeFileAsync(local, imgData, {encoding: 'binary'}).then(doSomething).catch(err => {
console.log("Error Downloading image");
// propagate error after logging it
throw err;
});
});
});
return Promise.all(promises);
}
原始问题:
- 永远不要用 for/in 迭代数组
- 诸如 local、url、id 之类的变量在过高的范围内声明,因此每次循环调用都会破坏这些值
- 错误没有被正确传播回顶层(调用者可能不知道某些错误)
注意事项:
- 在请求- promise 库中,使用
request.get().then()
将自动为您检查 2xx 状态,如果不是则拒绝,因此您可以在使用.then()
时删除该代码而不是简单的回调。 - 一般来说,您不希望将 promise 与普通回调混合使用,因为错误传播变得困难。所以,我创建了一个 promise 版本
fs.writeFile()
.如果使用 Bluebird promise 库,它将立即生成整个模块的 promise 版本。 - 从初始数组生成一对一数组是
.map()
被设计来做。它还为您提供了一个函数闭包,因此您不必制作自己的 IIFE。 - 循环中异步回调中使用的变量必须适当限定范围(尽可能局部),这样它们就不会被循环的其他迭代覆盖。
关于javascript - 将参数传递给循环创建的 Promise,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45905374/