【问题标题】:Using an async function in Array.find()在 Array.find() 中使用异步函数
【发布时间】:2022-05-02 23:50:47
【问题描述】:

似乎我无法使用异步函数作为 Array.find() 的第一个参数。我不明白为什么这段代码不能工作,到底发生了什么?


function returnsPromise() {
  return new Promise(resolve => resolve("done"));
}

async function findThing() {
  const promiseReturn = await returnsPromise();
  return promiseReturn;
}

async function run() {
  const arr = [1, 2];
  const found = await arr.find(async thing => {
    const ret = await findThing();
    console.log("runs once", thing);
    return false;
  });
  console.log("doesn't wait");
}

run();

https://codesandbox.io/s/zk8ny3ol03

【问题讨论】:

  • 您已经标记了谓词async,但这并不意味着Array.find() 是异步的。
  • 好的,简明扼要,谢谢。
  • @obermillerk 那行不通。这不仅在语法上不正确,find 也不是异步的,所以在任何情况下你都不能“等待”它完成。

标签: javascript


【解决方案1】:

简单地说,find 不期望返回一个承诺,因为它不是用于异步的东西。它循环遍历数组,直到其中一个元素导致返回一个真值。一个对象,包括一个 Promise 对象,是真实的,因此查找停止在第一个元素上。

如果您想要一个异步等效的 find,您需要自己编写。您需要考虑的一个问题是,您是要并行运行,还是要按顺序运行,在进入下一个索引之前进行阻塞。

例如,这是一个并行运行它们的版本,然后一旦所有的承诺都得到解决,它就会找到第一个产生真值的。

async function findAsync(arr, asyncCallback) {
  const promises = arr.map(asyncCallback);
  const results = await Promise.all(promises);
  const index = results.findIndex(result => result);
  return arr[index];
}

//... to be used like:

findAsync(arr, async (thing) => {
  const ret = await findThing();
  return false;
})

【讨论】:

  • 好吧,这就是我的怀疑。我用谷歌搜索了很多,在任何地方都找不到关于这个的任何信息。是否记录了 find 可以与这样的异步函数一起使用?
  • 据记录,.find 循环遍历数组,直到元素返回一个真实值,并且记录了异步函数返回承诺。这只是这两件事的相互作用。
  • @LauriePoulter Array.protoype 中没有任何东西是异步的
  • Promise.all 将等待每个承诺解决。最好等到一个承诺真正解决。
  • 使用Promise.race 可能会有所帮助,如果您正在寻找单数匹配,这将有助于比Promise.all 更快地完成此任务。
【解决方案2】:

这是一个按顺序运行的 TypeScript 版本:

async function findAsyncSequential<T>(
  array: T[],
  predicate: (t: T) => Promise<boolean>,
): Promise<T | undefined> {
  for (const t of array) {
    if (await predicate(t)) {
      return t;
    }
  }
  return undefined;
}

【讨论】:

    【解决方案3】:

    注意Array.prototype.filter 是同步的,因此它不支持异步行为,这可能对您有所帮助。我认为这同样适用于“查找”属性。您始终可以定义自己的异步属性 :) 希望这会有所帮助!

    【讨论】:

      【解决方案4】:

      其他答案提供了两种解决方案:

      1. 并行运行 Promise 并在返回索引之前等待所有内容
      2. 按顺序运行 Promise,因此在继续执行下一个 Promise 之前等待每一个 Promise。

      假设您有五个在不同时间完成的承诺:第一个在一秒后,第二个在两秒后等等......第五个在五秒后。

      如果我正在寻找三秒后完成的那个:

      1. 第一个解决方案将等待 5 秒,直到所有承诺都得到解决。然后它会寻找匹配的那个。
      2. 第二个将评估前三个匹配项(1 + 2 + 3 = 6 秒),然后 返回。

      这是通常应该更快的第三个选项:并行运行 Promise,但只等到第一场比赛(“赛跑”):3 秒

      function asyncFind(array, findFunction) {
        return new Promise(resolve => {
          let i = 0;
          array.forEach(async item => {
            if (await findFunction(await item)) {
              resolve(item);
              return;
            }
            i++;
            if (array.length == i) {
              resolve(undefined);
            }
          });
        });
      }
      
      //can be used either when the array contains promises
      var arr = [asyncFunction(), asyncFunction2()];
      await asyncFind(arr, item => item == 3);
      
      //or when the find function is async (or both)
      var arr = [1, 2, 3];
      await asyncFind(arr, async item => {
          return await doSomething(item);
      }
      

      在查找不存在的项目时,解决方案 1 和 3 将花费相同的时间(直到评估所有承诺,这里是 5 秒)。顺序方法(解决方案 2)需要 1+2+3+4+5 = 15 秒。

      演示:https://jsfiddle.net/Bjoeni/w4ayh0bp

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-11-10
        • 1970-01-01
        • 2019-01-20
        • 1970-01-01
        • 2020-08-13
        • 1970-01-01
        • 2020-03-09
        • 2022-11-16
        相关资源
        最近更新 更多