如何使用异步化简器来构建一系列承诺



下面是一个构建数据库查询的函数:

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

此函数返回在解决之前不应执行的承诺。

现在我们使用该函数

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 .

不要使用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')
}

最新更新