如何使用异步减速器构建承诺数组?

时间:2019-02-06 20:10:45

标签: javascript

这是构建数据库查询的功能:

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: []
  })
)

此函数返回的promise必须在解决后才能执行。

现在我们使用该功能

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

问题是:

  • 在我们调用Promise.all之前执行查询。
  • firstDbQueries查询不会在secondDbQueries引起错误之前执行。

编辑

如评论中所建议,我尝试不使用reduce,而是使用for … of循环。

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

这仍然会产生与带有reduce的先前版本完全相同的问题。

1 个答案:

答案 0 :(得分:3)

请勿使用async减速器。尤其是不要建立承诺。或一堆东西以后再运行。在许多级别上这都是错误的。

我想您正在寻找类似的东西

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