简体   繁体   中英

How to use an async reducer to build an array of promises?

Here is a function to build db queries:

const buildDbQueries = async elements => elements.reduce(
  async (acc, element) => {
    // wait for the previous reducer iteration
    const { firstDbQueries, secondDbQueries } = await acc
    const asyncStuff = await someApi(element)

    // leave if the API does not return anything
    if (!asyncStuff) return { firstDbQueries, secondDbQueries }

    // async db query, returns a Promise
    const firstDbQuery = insertSomethingToDb({ 
      id: asyncStuff.id, 
      name: asyncStuff.name
    })

    // another async db query, returns a Promise 
    // have to run after the first one
    const secondDbQuery = insertAnotherthingToDb({
      id: element.id, 
      name: element.name,
      somethingId: asyncStuff.id
    })

    return {
      firstDbQueries: [...firstDbQueries, firstDbQuery],
      secondDbQueries: [...secondDbQueries, secondDbQuery]
    }
  },
  // initial value of the accumulator is a resolved promise
  Promise.resolve({
    firstDbQueries: [],
    secondDbQueries: []
  })
)

This function returns promises which should not be executed until they are resolved.

Now we use that function

const myFunc = async elements => {

  const { firstDbQueries, secondDbQueries } = await buildDbQueries(elements)

  // we don't want any query to run before this point

  await Promise.all(firstDbQueries)
  console.log('Done with the first queries')

  await Promise.all(secondDbQueries)
  console.log('Done with the second queries')
}

The problems are:

  • the queries are executed before we call Promise.all .
  • the firstDbQueries queries are not executed before the secondDbQueries causing errors.

EDIT

As suggested in a comment, I tried not to use reduce , but a for … of loop.

const buildDbQueries = async elements => {
  const firstDbQueries = []
  const secondDbQueries = []

  for (const element of elements) {
    const asyncStuff = await someApi(element)

    // leave if the API does not return anything
    if (!asyncStuff) continue

    // async db query, returns a Promise
    const firstDbQuery = insertSomethingToDb({ 
      id: asyncStuff.id, 
      name: asyncStuff.name
    })

    // another async db query, returns a Promise 
    // have to run after the first one
    const secondDbQuery = insertAnotherthingToDb({
      id: element.id, 
      name: element.name,
      somethingId: asyncStuff.id
    })

    firstDbQueries.push(firstDbQuery)
    secondDbQueries.push(secondDbQuery)

  }

  return { firstDbQueries, secondDbQueries }
}

This still produces the exact same problems as the previous version with reduce .

Don't use an async reducer. Especially not to build an array of promises. Or an array of things to run later. This is wrong on so many levels.

I guess you are looking for something like

function buildDbQueries(elements) {
  return elements.map(element =>
    async () => {
      const asyncStuff = await someApi(element)
      // leave if the api doesn't return anything
      if (!asyncStuff) return;

      await insertSomethingToDb({ 
        id: asyncStuff.id, 
        name: asyncStuff.name
      });
      return () =>
        insertAnotherthingToDb({
          id: element.id, 
          name: element.name,
          somethingId: asyncStuff.id
        })
      ;
    }
  );
}

async function myFunc(elements) {
  const firstQueries = buildDbQueries(elements)

  // we don't want any query to run before this point

  const secondQueries = await Promise.all(firstQueries.map(query => query()));
  //                              this call actually runs the query ^^^^^^^
  console.log('Done with the first queries');

  await Promise.all(secondQueries.map(query => query()));
  //         this call actually runs the query ^^^^^^^
  console.log('Done with the second queries')
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM