【问题标题】:Asynchronously loop through array with key使用键异步循环遍历数组
【发布时间】:2021-08-06 20:34:59
【问题描述】:

我有一组客户,我想遍历他们并为每个客户分配一个属性。添加属性的客户对象通过 details 函数返回

for (let i = 0; i < customers.length; i++)
{
    customers[i] = await details(customers[i]);
}

这就是我目前的做法,但它是同步的。我希望列表中的所有对象一次异步完成。我正在尝试使用异步库,但它只为我提供了循环内可用的客户对象,因此客户数组永远不会改变。

async.forEachOf(customers, (customer, finished) async =>
{
    customer = await details(customer);
    finished();
});

我该怎么做呢?谢谢

【问题讨论】:

  • 我认为您的要求不是很清楚。第一个代码示例是我认为您想要做的事情,您的描述并不清楚为什么它是错误的。
  • 如果拿第一个例子,改成customers[i].details = await details(customers[i]);,是不是更接近你想要的?
  • 是的,它完全符合我的需要,但它是同步的。假设有 10 个客户,我希望所有 10 个客户同时完成。 details 函数返回带有添加属性的客户,因此我想通过使用 customers[i] 更新该客户,而在第二个示例中,客户是在函数中定义的,因此在我无法返回带有添加功能的客户列表之后
  • s/synchronous/sequential。您使用“同步”错误。
  • 哦,您希望它们同时完成。这很简单,我会发表评论

标签: javascript asynchronous async.js


【解决方案1】:

您不需要第三方库来完成此操作。只需使用Promise.all()Array.prototype.map()

const customerDetails = await Promise.all(
  customers.map(customer => details(customer))
);

【讨论】:

    【解决方案2】:

    library is not required 异步迭代项目数组。

    例如,异步处理这个数组:

    const arr = [1, 2, 3];

    首先,从一个数组创建一个生成器,以便您可以为数组中的每个项目产生执行:

    /**
     * A convenience function for creating a ES2015 Generator to iterate over
     * items in an array.
     *
     * @template T
     * @param {T[]} value
     */
    function* toIterator<T>(value: T[]) {
      const generator = value[Symbol.iterator]();
      for (let generated of generator) {
        yield generated;
      }
    }
    

    然后,异步迭代数组的关键是在提供的handlerresolve 上调用自己的函数。

    
    /**
     * Provides the ability to process an iterator's values asynchronously
     * with a predicate, `hander`.
     *
     * @template T
     * @template TReturn
     * @template TNext
     * @param {Generator<T, TReturn, TNext>} generator
     * @param {(arg: T) => Promise<TReturn>} handler
     * @returns
     */
    async function iterateAsynchronously<T, TReturn, TNext>(
      generator: Generator<T, TReturn, TNext>,
      handler: (arg: T) => Promise<TReturn>
    ) {
      const iterate: () => Promise<TReturn> = async () => {
        const generated = generator.next();
        if (generated.done) {
          return generated.value;
        }
        return handler(generated.value).then(iterate);
      };
      return iterate();
    }
    

    如果您愿意,可以使用其中一种或两种方法扩展全局数组。 Another example.

    这将允许您在异步处理项目后将更多数组方法链接在一起:

    const run = async () => {
      const arr = [1, 2, 3, 4];
      console.log((await arr.iterateAsynchronously((val) => new Promise(r => {
        setTimeout(r, 2000);
        console.log('Processing', val);
      }))).reverse());
    }
    run();
    

    在这种情况下,由于数组原型被修改为返回数组本身的函数扩展,因此您可以像以前一样将数组方法链接在一起。

    【讨论】:

      【解决方案3】:

      此代码将为每个客户一个接一个地运行details(),而无需等待前一个客户。你创建了一个 promise 数组并在最后等待它们

      const promises = [];
      for (let i = 0; i < customers.length; i++) {
          let p = new Promise(async (resolve, reject) => {
            customers[i] = await details(customers[i]);
            resolve();
          });
          promises.push(p);
      }
      await Promise.all(promises);
      

      【讨论】:

      • 还是错了。这就是为什么承诺构造是antipattern。你没有正确处理错误。如果您只是 let p = details(customers[i]),那将是正确的,尽管仍然比必要的冗长得多。
      猜你喜欢
      • 1970-01-01
      • 2017-12-12
      • 2017-05-10
      • 1970-01-01
      • 2015-02-18
      • 1970-01-01
      • 2023-03-26
      • 2014-07-12
      • 2012-10-12
      相关资源
      最近更新 更多