将参数传递给循环创建的Promises

时间:2017-08-27 13:19:19

标签: 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)次。

我尝试了几件事,但无法接近解决方案。有什么基本的我做错了或者相当简单的东西?非常感谢一些帮助!

1 个答案:

答案 0 :(得分:1)

我建议像这样简化:

// 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. 迭代数组
  3. 诸如local,url,id之类的变量被声明为范围太高,因此每次调用循环都会破坏这些值
  4. 错误未正确传播回顶层(来电者根本不知道某些错误)
  5. 注意:

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