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

时间:2011-12-07 10:30:40

标签: javascript node.js

我就是这样做的:

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

有什么好处吗?如何避免那些意大利面条的代码?

5 个答案:

答案 0 :(得分:30)

签出async库,它是为了控制流(异步东西),它有很多方法用于数组:每个,过滤器,映射。查看github上的文档。以下是您可能需要的内容:

每个(arr,迭代器,回调)

并行地对数组中的每个项应用迭代器函数。使用列表中的项目调用迭代器,并在完成时调用它。如果迭代器将错误传递给此回调,则会立即调用each函数的主回调并显示错误。

eachSeries(arr,iterator,callback)

each相同,只有迭代器才会串行应用于数组中的每个项目。只有当前的迭代器完成处理后才会调用下一个迭代器。这意味着迭代器函数将按顺序完成。

答案 1 :(得分:17)

如某些答案所指出,可以使用" async"图书馆。但有时你不想在你的代码中引入新的依赖。下面是循环和等待完成某些异步函数的另一种方法。

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

现在,正在运行node iterasync.js将等待大约三秒钟,然后打印[ 'ONE', 'TWO', 'THREE' ]。这是一个简单的例子,但它可以扩展到处理许多情况。

答案 2 :(得分:9)

正如所指出的,你必须使用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) })

答案 3 :(得分:3)

处理数组(或任何其他可迭代的)异步迭代的最简单方法是使用await运算符(仅在异步函数中)和for循环。

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

您可以使用库来转换您可能需要的任何接受回调的函数来返回promises。

答案 4 :(得分:0)

在现代 JavaScript 中,有一些有趣的方法可以将 Array 扩展为异步 itarable 对象。

在这里,我想展示一个全新类型 > klout@0.1.0 dev > next dev Loaded env from [path]/.env.local 的骨架,它通过继承它的优点来扩展 AsyncArray 类型只是为了成为一个异步可迭代数组。

这仅在现代引擎中可用。下面的代码使用了最新的噱头,如 private instance fieldsfor await...of

如果您不熟悉它们,那么我建议您提前查看上面链接的主题。

Array

所以一个异步可迭代数组必须包含承诺。只有这样,它才能返回一个迭代器对象,该对象在每次 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}); }; }; 调用时都会返回一个承诺,最终将 next() 转化为 resolve{value : "whatever", done: false} 之类的对象。所以基本上所有返回的东西都是一个承诺。 {done: true} 抽象解压缩其中的值并将其提供给我们。

现在正如我之前提到的,这个 await 类型,因为从 AsyncArray 扩展,允许我们使用我们熟悉的那些 Array 方法。这应该会简化我们的工作。

让我们看看会发生什么;

Array