简体   繁体   English

在承诺和异步函数中使用for..in循环

[英]Using for..in loop with Promises and asynchronous functions

I need to be able to loop through an object of images and perform an asynchronous function on each image one at a time. 我需要能够遍历图像的一个对象,并且一次对每个图像执行一个异步功能。 I have it kind of working if I convert the images object to an array but I want to do this with a for...in loop so I can use the image keys as well. 如果将图像对象转换为数组,则可以正常工作,但是我想使用for...in循环来执行此操作,因此也可以使用图像键。 I also need to be able to perform an action at the end as I am currently. 我还需要能够像现在这样在最后执行操作。

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

The order isn't important but having them call one after the other is, and having an onComplete or end call is also needed. 顺序并不重要,但是让它们一个接一个地调用是很重要的,并且还需要进行onComplete结束调用。 I just can't get my head round it, can anyone help? 我只是无法理解,有人可以帮忙吗?

Using reduce is a nice way to do this. 使用reduce是一种很好的方法。 You can pass the key/value pairs in with Object.entries 您可以将键/值对与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")) 

If, on the other hand, your async function returned its own promise, this would be a little easier on the eyes: 另一方面,如果您的异步函数返回了自己的诺言,那么这看起来会容易一些:

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

Just use Object.keys or Object.entries instead of Object.values if you also need to access the keys. 如果还需要访问键,则只需使用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);

Notice that the new Promise wrapper should be directly around the setTimeout call, inside asynchronousImageFunction ; 请注意, new Promise包装器应直接在setTimeout调用周围,位于asynchronousImageFunction setTimeout内部; that makes it easier to use and you need to pass less callbacks around. 这使它更易于使用,并且您需要传递的回调更少。

The alternative that would let you use a real for … in loop is async / await syntax: 让您将real for … in循环的替代方法是async / await语法:

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

You aren't going to be able to do this with a for...in , really. 实际上,您将无法使用for...in来进行此操作。 Loops like for...in and for...of can't be instructed to wait for asynchronous events before beginning the next iteration. 不能指示for...infor...offor...in循环在开始下一次迭代之前要等待异步事件。

What you have to do is implement something that behaves like the desired loop, but does wait for async events. 您所要做的是实现一些行为,该行为的行为类似于所需的循环,但确实要等待异步事件。 What you've done behaves like a for...of over an array. 您所做的工作就像一个数组的for...of You can make the keys available by doing something like what Mark_M described. 您可以通过执行Mark_M描述的操作来使键可用。

This is, however, a very common operation, and has been abstracted (along with many other asynchronous operations) in libraries like async , enabling you to skip this annoyance and just write what you want: 但是,这是一个非常常见的操作,并且已在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');
});

Documentation for async.eachOfSeries can be found here . 可以在这里找到async.eachOfSeries的文档。

You'll notice I didn't use a Promise here. 您会发现我在这里没有使用Promise。 This is largely because your asynchronousImageFunction is callback-based, as is the async library itself. 这主要是因为您的asynchronousImageFunction功能以及async库本身都是基于回调的。 My advice when dealing with async code is to try not flip back and forth between styles too often, or stuff gets hella confusing. 在处理异步代码时,我的建议是尽量不要在样式之间来回切换,否则东西会令人困惑。

If you are able to make CommonJS modules work in your environment, there are promise-based variants of async . 如果您能够使CommonJS模块在您的环境中工作,那么可以使用基于承诺的async变体。 One of my favorites is here . 我的最爱之一在这里 With it, you can make the loop itself promise-based: 有了它,您可以使循环本身基于承诺:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM