简体   繁体   English

在数组(或对象)上迭代异步的最聪明/最干净的方法是什么?

[英]Whats the smartest / cleanest way to iterate async over arrays (or objs)?

Thats how I do it:我就是这样做的:

function processArray(array, index, callback) {
    processItem(array[index], function(){
        if(++index === array.length) {
            callback();
            return;
        }
        processArray(array, index, callback);
    });
};

function processItem(item, callback) {
    // do some ajax (browser) or request (node) stuff here

    // when done
    callback();
}

var arr = ["url1", "url2", "url3"];

processArray(arr, 0, function(){
    console.log("done");
});

Is it any good?有什么好处吗? How to avoid those spaghetti'ish code?如何避免那些意大利面条式的代码?

Checkout the async library, it's made for control flow (async stuff) and it has a lot of methods for array stuff: each, filter, map.查看异步库,它是为控制流(异步内容)而设计的,它有很多用于数组内容的方法:每个、过滤器、映射。 Check the documentation on github.查看github上的文档。 Here's what you probably need:以下是您可能需要的:

each(arr, iterator, callback)每个(arr,迭代器,回调)

Applies an iterator function to each item in an array, in parallel.将迭代器函数并行应用于数组中的每个项目。 The iterator is called with an item from the list and a callback for when it has finished.使用列表中的一个项目和完成时的回调调用迭代器。 If the iterator passes an error to this callback, the main callback for the each function is immediately called with the error.如果迭代器将错误传递给此回调,则立即调用each函数的主回调并带有错误。

eachSeries(arr, iterator, callback)每个系列(arr,迭代器,回调)

The same as each only the iterator is applied to each item in the array in series.each相同,只有迭代器依次应用于数组中的每个项目。 The next iterator is only called once the current one has completed processing.只有在当前迭代器完成处理后才会调用下一个迭代器。 This means the iterator functions will complete in order.这意味着迭代器函数将按顺序完成。

As pointed in some answer one can use "async" library.正如某些答案中所指出的,可以使用“异步”库。 But sometimes you just don't want to introduce new dependency in your code.但有时您只是不想在代码中引入新的依赖项。 And below is another way how you can loop and wait for completion of some asynchronous functions.下面是另一种循环和等待一些异步函数完成的方法。

var items = ["one", "two", "three"];

// This is your async function, which may perform call to your database or
// whatever...
function someAsyncFunc(arg, cb) {
    setTimeout(function () {
        cb(arg.toUpperCase());
    }, 3000);
}

// cb will be called when each item from arr has been processed and all
// results are available.
function eachAsync(arr, func, cb) {
    var doneCounter = 0,
        results = [];
    arr.forEach(function (item) {
        func(item, function (res) {
            doneCounter += 1;
            results.push(res);
            if (doneCounter === arr.length) {
                cb(results);
            }
        });
    });
}

eachAsync(items, someAsyncFunc, console.log);

Now, running node iterasync.js will wait for about three seconds and then print [ 'ONE', 'TWO', 'THREE' ] .现在,运行node iterasync.js将等待大约三秒钟,然后打印[ 'ONE', 'TWO', 'THREE' ] This is a simple example, but it can be extended to handle many situations.这是一个简单的例子,但它可以扩展到处理许多情况。

As correctly pointed out, you have to use setTimeout, for example:正如正确指出的那样,您必须使用 setTimeout,例如:

each_async = function(ary, fn) {
    var i = 0;
    -function() {
        fn(ary[i]);
        if (++i < ary.length)
            setTimeout(arguments.callee, 0)
    }()
}


each_async([1,2,3,4], function(p) { console.log(p) })

The easiest way to handle async iteration of arrays (or any other iterable) is with the await operator (only in async functions) and for of loop.处理数组(或任何其他可迭代对象)的异步迭代的最简单方法是使用 await 运算符(仅在异步函数中)和 for of 循环。

 (async function() { for(let value of [ 0, 1 ]) { value += await(Promise.resolve(1)) console.log(value) } })()

You can use a library to convert any functions you may need which accept callback to return promises.您可以使用库来转换您可能需要的任何接受回调以返回承诺的函数。

In modern JavaScript there are interesting ways to extend an Array into an async itarable object.在现代 JavaScript 中,有一些有趣的方法可以将 Array 扩展为异步 itarable 对象。

Here I would like to demonstrate a skeleton of a totally new type AsyncArray which extends the Array type by inheriting it's goodness just to become an async iterable array.在这里,我想展示一个全新类型AsyncArray的骨架,它通过继承它的优点来扩展Array类型,只是为了成为一个异步可迭代数组。

This is only available in the modern engines.这仅适用于现代发动机。 The code below uses the latest gimmicks like the private instance fields and for await...of .下面的代码使用了最新的噱头,如私有实例字段for await...of

If you are not familiar with them then I would advise you to have a look at the above linked topics in advance.如果您不熟悉它们,那么我建议您提前查看上述链接主题。

class AsyncArray extends Array {
  #INDEX;
  constructor(...ps){
    super(...ps);
    if (this.some(p => p.constructor !== Promise)) {
      throw "All AsyncArray items must be a Promise";
    }
  }
  [Symbol.asyncIterator]() {
    this.#INDEX = 0;
    return this;
  };
  next() {
    return this.#INDEX < this.length ? this[this.#INDEX++].then(v => ({value: v, done: false}))
                                     : Promise.resolve({done: true});
  };
};

So an Async Iterable Array must contain promises.所以一个 Async Iterable Array 必须包含 promise。 Only then it can return an iterator object which with every next() call returns a promise to eventually resolve into an object like {value : "whatever", done: false} or {done: true} .只有这样它才能返回一个迭代器对象,每个next()调用都会返回一个承诺,最终resolve为一个对象,如{value : "whatever", done: false}{done: true} So basically everything returned is a promise here.所以基本上所有返回的东西都是一个承诺。 The await abstraction unpacks the value within and gives it to us. await抽象解压缩其中的值并将其提供给我们。

Now as I mentioned before, this AsyncArray type, since extended from Array , allows us to use those Array methods we are familiar with.正如我之前提到的,这个AsyncArray类型,因为从Array扩展而来,允许我们使用我们熟悉的那些 Array 方法。 That should simplify our job.这应该会简化我们的工作。

Let's see what happens;让我们看看发生了什么;

 class AsyncArray extends Array { #INDEX; constructor(...ps){ super(...ps); if (this.some(p => p.constructor !== Promise)) { throw "All AsyncArray items must be a Promise"; } } [Symbol.asyncIterator]() { this.#INDEX = 0; return this; }; next() { return this.#INDEX < this.length ? this[this.#INDEX++].then(v => ({value: v, done: false})) : Promise.resolve({done: true}); }; }; var aa = AsyncArray.from({length:10}, (_,i) => new Promise(resolve => setTimeout(resolve,i*1000,[i,~~(Math.random()*100)]))); async function getAsycRandoms(){ for await (let random of aa){ console.log(`The Promise at index # ${random[0]} gets resolved with a random value of ${random[1]}`); }; }; getAsycRandoms();

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

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