【问题标题】:Whats the smartest / cleanest way to iterate async over arrays (or objs)?在数组(或 objs)上迭代异步的最聪明/最干净的方法是什么?
【发布时间】:2011-12-07 10:30:39
【问题描述】:

我就是这样做的:

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

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

【问题讨论】:

  • 使用jQuery,可以使用$.each([...], function() {...});,其他JS库中也会有类似的方法。
  • 是的,但它们是同步的。 Aron 正在询问异步循环,这是一个更有趣的问题!
  • 2016,我今天用过这个。应该看看 ES6 但是...谢谢!

标签: javascript node.js


【解决方案1】:

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

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

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

每个系列(arr、迭代器、回调)

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

【讨论】:

  • 函数被称为“each”和“eachSeries”github.com/caolan/async#each
  • 请记住,尽管@LaurentDebricon,他的评论是在您的评论之前两年写的
  • 如何在数组上无限循环,例如 async.eachSeries([1,2,3]) 我想在 3 之后返回 1 ,该怎么做
【解决方案2】:

正如在某些答案中指出的那样,可以使用“异步”库。但有时您只是不想在代码中引入新的依赖项。下面是另一种循环和等待一些异步函数完成的方法。

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' ]。这是一个简单的例子,但它可以扩展到处理许多情况。

【讨论】:

  • 令人难以置信的是,这个答案对这个评论的支持为零。
  • 很好的解决方案!
  • 输出不一定是1,2,3的顺序。
  • 不处理空数组大小写,计数器似乎没有必要,因为你有“结果”的长度
【解决方案3】:

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

【讨论】:

  • 为什么第二个function()前面有一个“-”(减号)?
  • @AaronDigulla -function() 导致立即函数被视为表达式。它与将函数包装在括号中并没有什么不同,这是使其成为有效表达式的另一种方式。见:stackoverflow.com/questions/13341698/…
【解决方案4】:

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

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

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

【讨论】:

  • 我发现这种方法更干净。
【解决方案5】:

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

在这里,我想展示一个全新类型 AsyncArray 的骨架,它通过继承它的优点来扩展 Array 类型,只是为了成为一个异步可迭代数组。

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

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

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 方法。这应该会简化我们的工作。

让我们看看会发生什么;

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-28
    • 2013-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-11
    相关资源
    最近更新 更多