【问题标题】:Using for..in loop with Promises and asynchronous functions将 for..in 循环与 Promises 和异步函数一起使用
【发布时间】:2018-04-03 18:04:08
【问题描述】:

我需要能够遍历图像对象并一次对每个图像执行异步功能。 如果我将图像对象转换为数组,我可以使用它,但我想使用 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);

顺序并不重要,但让他们一个接一个地调用很重要,并且还需要一个 onCompleteend 调用。 我就是想不通,有人能帮忙吗?

【问题讨论】:

  • 您是在浏览器还是节点中执行此操作?
  • 在 Cordova 应用程序中......浏览器非常重要。

标签: javascript for-loop asynchronous promise


【解决方案1】:

使用reduce 是一种很好的方法。您可以使用 Object.entries 传递键/值对

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"))

另一方面,如果你的 async 函数返回了它自己的 promise,这看起来会更容易一些:

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"))

【讨论】:

  • @juliusbangert 我刚刚添加了这个(对不起,我没有在你的 OP 中注意到)。你可以在上面加上then(),因为它会返回最后一个承诺。
  • 最终我需要能够使用 polyfill 支持 ES5,考虑到使用 Object.entries 最好的方法是什么(我意识到 Promise 需要 pollyfill)
  • @juliusbangert Object.entries 的这个 polyfil 很好用而且简单易用:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
【解决方案2】:

如果您还需要访问密钥,只需使用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");
})();

【讨论】:

  • 使用异步有什么缺点?我的想法是我需要使用 Promise。
  • @juliusbangert 没有缺点(也许除了与旧引擎的兼容性,但转译器解决了这个问题)。而且你仍然会使用 Promise,asynchronousImageFunction 仍然需要在 sn-p 中返回一个 await
【解决方案3】:

你不能用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 库本身也是如此。在处理异步代码时,我的建议是不要过于频繁地在样式之间来回切换,否则事情会变得混乱。

如果您能够使 CommonJS 模块在您的环境中工作,那么async 有基于承诺的变体。我的最爱之一是here。有了它,你可以让循环本身基于 Promise:

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');
    });

【讨论】:

    猜你喜欢
    • 2021-11-13
    • 1970-01-01
    • 2015-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-07
    • 2013-08-14
    • 1970-01-01
    相关资源
    最近更新 更多