使用带有Promises和异步函数的for..in循环

时间:2018-04-03 18:04:09

标签: javascript for-loop asynchronous promise

我需要能够遍历图像对象并一次一个地对每个图像执行异步功能。 如果我将图像对象转换为数组但是我希望使用 for...in 循环执行此操作,那么我可以使用它,所以我也可以使用图像键。我还需要能够像我目前一样在最后执行一项行动。

var images = {
  ABD: '1464685684713583388647.jpg',
  ABY: '1457524543088191607099.jpg',
  ADV: '1478877365443818880647.jpg',
  AFD: '1457527861824290195088.jpg',
}
var imagesArray = Object.values(images);
var len = imagesArray.length;

function asynchronousImageFunction (key, image, onSuccess, onFail) {
  setTimeout(function () {
    console.log(key);
    console.log(image);
    onSuccess();
  }, Math.random() * 1000)
}

(function loop(i) {
  if (i < len) {
    new Promise(function (resolve, reject) {
      asynchronousImageFunction ('key', imagesArray[i], resolve, reject);
    }).then(loop.bind(null, i+1));
  } else {
    console.log('end');
  }
})(0);

订单并不重要,但要让它们一个接一个地调用,并且还需要进行 onComplete end 调用。 我无法理解它,任何人都可以帮忙吗?

3 个答案:

答案 0 :(得分:2)

使用reduce是一种很好的方法。您可以使用Object.entries

传递键/值对

&#13;
&#13;
var images = {
  ABD: '1464685684713583388647.jpg',
  ABY: '1457524543088191607099.jpg',
  ADV: '1478877365443818880647.jpg',
  AFD: '1457527861824290195088.jpg',
}

function asynchronousImageFunction(key, image, onSuccess, onFail) {
  setTimeout(function() {
    console.log(key);
    console.log(image);
    onSuccess();
  }, 1000)
}

Object.entries(images).reduce((a, [key, value]) => {
  return a.then(() => new Promise((resolve, reject) => {
    asynchronousImageFunction(key, value, resolve, reject);
  }))}, Promise.resolve())
.then(() => console.log("end"))
&#13;
&#13;
&#13;

另一方面,如果您的异步函数返回了自己的承诺,那么这对眼睛来说会更容易:

&#13;
&#13;
var images = {
  ABD: '1464685684713583388647.jpg',
  ABY: '1457524543088191607099.jpg',
  ADV: '1478877365443818880647.jpg',
  AFD: '1457527861824290195088.jpg',
}

function asynchronousImageFunction(key, image, onSuccess, onFail) {
  return new Promise((resolve, reject) => {
    setTimeout(function() {
      console.log(key);
      console.log(image);
      resolve();
    }, 1000)
  })
}

Object.entries(images).reduce((a, [key, value]) => 
  a.then(() => asynchronousImageFunction(key, value))
  , Promise.resolve())
  .then(() => console.log("end"))
&#13;
&#13;
&#13;

答案 1 :(得分:0)

如果您还需要访问密钥,只需使用Object.keysObject.entries代替Object.values

var imageKeysArray = Object.key(images);
var len = imagesArray.length;
(function loop(i) {
  if (i < len) {
    var key = imageKeysArray[i];
    var value = images[key];
    asynchronousImageFunction(key, value).then(loop.bind(null, i+1));
  } else {
    console.log('end');
  }
})(0);

请注意,new Promise包装应该直接围绕setTimeout调用,位于asynchronousImageFunction内;这使得它更容易使用,你需要传递较少的回调。

允许您使用真实for … in循环的替代方法是async / await语法:

(async function loop() {
  for (const key in images)
    await asynchronousImageFunction(key, images[key]);
  console.log("end");
})();

答案 2 :(得分:0)

你真的无法使用for...in执行此操作。在开始下一次迭代之前,不能指示for...infor...of等循环等待异步事件。

您需要做的是实现与所需循环类似的行为,但 等待异步事件。您所做的事情就像数组上的for...of一样。您可以通过执行Mark_M描述的操作来使密钥可用。

然而,这是一个非常常见的操作,并且已经在async这样的库中被抽象(以及许多其他异步操作),使您可以跳过这个烦恼并只写下您想要的内容:

var images = {
  ABD: '1464685684713583388647.jpg',
  ABY: '1457524543088191607099.jpg',
  ADV: '1478877365443818880647.jpg',
  AFD: '1457527861824290195088.jpg',
}

function asynchronousImageFunction(key, image, callback) {
  setTimeout(function () {
    console.log(key);
    console.log(image);
    // Usual convention of `async` callbacks. The first argument should be
    // null/undefined/omitted if no error occurred:
    callback();
    // If there was an error, you would instead do this:
    // callback(err);
  }, Math.random() * 1000)
}

async.eachOfSeries(images, function(image, key, callback) {
    asynchronousImageFunction(key, image, callback);
}, function(err) {
    console.log('end');
});

可以找到async.eachOfSeries的文档here

你会注意到我在这里没有使用Promise。这主要是因为asynchronousImageFunction是基于回调的,async库本身也是如此。我在处理异步代码时的建议是不要经常在样式之间来回切换,否则会让hella混淆。

如果您能够在您的环境中使CommonJS模块工作,则存在基于承诺的async变体。我最喜欢的一个是here。有了它,你可以使循环本身基于承诺:

const pasync = require('pasync');

var images = {
  ABD: '1464685684713583388647.jpg',
  ABY: '1457524543088191607099.jpg',
  ADV: '1478877365443818880647.jpg',
  AFD: '1457527861824290195088.jpg',
}

function asynchronousImageFunction(key, image) {
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log(key);
            console.log(image);
            resolve();
        }, Math.random() * 1000);
    });
}

pasync.eachOfSeries(
    images,
    (image, key) => asynchronousImageFunction(key, image)
)
    .then(() => {
        console.log('end');
    });