Using an async function in Array.find()

15,298

Solution 1

Simply put, find does not expect a promise to be returned, because it is not intended for asynchronous things. It loops through the array until one of the elements results in a truthy value being returned. An object, including a promise object, is truthy, and so the find stops on the first element.

If you want an asynchronous equivalent of find, you'll need to write it yourself. One consideration you'll want to have is whether you want to run things in parallel, or if you want to run them sequentially, blocking before you move on to the next index.

For example, here's a version that runs them all in parallel, and then once the promises are all resolved, it finds the first that yielded a truthy value.

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

Solution 2

Here is a TypeScript version that runs sequentially:

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;
}
Share:
15,298
Laurie Poulter
Author by

Laurie Poulter

Updated on June 19, 2022

Comments

  • Laurie Poulter
    Laurie Poulter almost 2 years

    It seems I'm unable to use an async function as the first argument to Array.find(). I can't see why this code would not work what is happening under the hood?

    
    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