javascript - 将参数传递给循环创建的 Promise

标签 javascript es6-promise

我正在编写一个函数,使用请求包批量下载图像。 下载部分有效,但我在将参数传递到 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);
}

原始问题:

  1. 永远不要用 for/in 迭代数组
  2. 诸如 local、url、id 之类的变量在过高的范围内声明,因此每次循环调用都会破坏这些值
  3. 错误没有被正确传播回顶层(调用者可能不知道某些错误)

注意事项:

  1. 在请求- promise 库中,使用 request.get().then()将自动为您检查 2xx 状态,如果不是则拒绝,因此您可以在使用 .then() 时删除该代码而不是简单的回调。
  2. 一般来说,您不希望将 promise 与普通回调混合使用,因为错误传播变得困难。所以,我创建了一个 promise 版本 fs.writeFile() .如果使用 Bluebird promise 库,它将立即生成整个模块的 promise 版本。
  3. 从初始数组生成一对一数组是 .map()被设计来做。它还为您提供了一个函数闭包,因此您不必制作自己的 IIFE。
  4. 循环中异步回调中使用的变量必须适当限定范围(尽可能局部),这样它们就不会被循环的其他迭代覆盖。

关于javascript - 将参数传递给循环创建的 Promise,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45905374/

相关文章:

javascript - 流畅的异步 api 与 ES6 代理 javascript

javascript - 在 bootstrap-ui datepicker 中允许 Moment.js 日期

javascript - Ajax 调用未响应重复请求

javascript - 迭代内部数据: [ ] in javascript

javascript - 使用 Promise 进行顺序加载

javascript - 我可以在另一个已解决的 promise 中解决一个 promise 吗?

javascript - 当子弹出窗口打开时禁用父窗口

javascript - 如何在 Cytoscape.JS 节点内容中设置多个文本行?

javascript - 在 Promise 链中提取函数

javascript - 创建一个用于长时间计算的加载屏幕