具有四个嵌套循环的异步等待

Async Await with four nested loops

我目前正在尝试 return 一个 JSON 对象数组,它要求我执行一个异步函数,然后执行四个嵌套的异步映射函数以填充实体数组。基本上,每个用户都有一个订单数组,每个订单都有一个项目数组,每个项目都有一个选项数组,每个选项都有一个值数组。我正在使用 loopback4 框架,因此一旦所有内容都已填充就无法执行 res.send 。该函数似乎在第一次等待时 return,但之后的任何等待,它都不会等待它,而是运行到函数的末尾。我尝试过使用 Promises 和 .thens(),但似乎无法弄清楚如何填充每个完全嵌套的实体,然后 return 填充实体的数组。我一直得到一个空数组。下面只有一个地图嵌套,但我什至不能让它填充到第一个嵌套和 return 这个,所以我决定不再继续。这是代码:

async getUserOrders2(@param.path.number('id') id: number): Promise<any> {
      if ( !this.user) {
        throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);
      }
      else if (this.user.id != id) {
        throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);
      }
      else  {
        let restaurantId = this.user.restaurantId
        let orderFrameArray = new Array<OrderFrame>()
        return this.restaurantRepository.orders(restaurantId as string).find()
        .then(async orders => {
          orders.map(async (val, key)=> {
            let orderFrame = new OrderFrame(val)
            orderFrame.itemArray = await this.orderRepository.orderItems(val.id).find()
            orderFrameArray.push(orderFrame)
          })
          orderFrameArray = await Promise.all(orderFrameArray)
          return orderFrameArray
        })
      }
}

在填充 orderFrameArray 之前,函数正在 returning。我需要四个嵌套的地图循环,而第一个循环不工作,所以我不知道如何做剩下的。任何帮助将不胜感激。

基于@Tomalaks 的解决方案,我尝试了以下方法,但它仍然只是 return 顶级数组并且没有嵌套任何内容:

    async getUserOrders2(@param.path.number('id') id: number): Promise<any> {
      if ( !this.user) {
        throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);
      }
      else if (this.user.id != id) {
        throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);
      }
      else  {
        let restaurantId = this.user.restaurantId
        let orderFrameArray = new Array<OrderFrame>()
        return this.restaurantRepository.orders(restaurantId as string).find()
          .then(orders => {Promise.all(orders.map(
          order => { 
          let orderFrame = new OrderFrame(order)
          orderFrame.itemArray = new Array<Item>()
          this.orderRepository.orderItems(order.id).find()
            .then(orderItems => Promise.all(orderItems.map(
            orderItem => {
            let itemFrame = new Item(orderItem)
            itemFrame.options = new Array<Option>()
            this.orderItemRepository.orderItemOptions(orderItem.id).find()
                .then(orderItemOptions => Promise.all(orderItemOptions.map(
                orderItemOption => { 
                let optionFrame = new Option(orderItemOption)
                optionFrame.values = new Array<Value>()
                this.orderItemOptionRepository.orderItemOptionValues(orderItemOption.id).find()
                    .then(orderItemOptionValues => Promise.all(orderItemOptionValues.map(
                    orderItemOptionValue => { 
                    let valueFrame = new Value(orderItemOptionValue)
                    optionFrame.values.push(valueFrame)})))
                itemFrame.options.push(optionFrame)})))
              orderFrame.itemArray.push(itemFrame)})))
            orderFrameArray.push(orderFrame)}))
          return orderFrameArray})
      }
    }

对于格式问题我深表歉意,我不确定如何最好地格式化它。我还有什么地方做错了吗?

感谢大家的回复。 @Tomalak 发布的答案是正确的。我只需要将整个函数括在方括号中,然后将 .then 放入 return 我制作的填充实体

当您在同一函数中使用 await 时,您 需要使用 async。如果嵌套函数中有await,则父函数不需要async

但是,在您的情况下,首先没有应该 async 的功能。

  • 在函数中等待任何结果没有任何好处,因为内部代码不依赖于任何中间结果。只是 return 你得到的承诺。
  • 不需要像 orderFrameArray 这样的中间结果变量,你让事情变得比等待单个订单并将它们推送到顶级变量的方法更难。
  • 像在 .map() 调用中那样在循环中使用 await 不利于性能。您基本上是以这种方式序列化数据库访问——下一个查询只会在当前查询完成 returned 后发送。这种菊花链使数据库无法处理多个并发请求。
  • getUserOrders2 不是 Promise<any>,而是 Promise<Array<OrderFrame>>
  • throw 无论如何都会终止函数,您可以在不使用 else if 的情况下对错误条件进行多次检查。这减少了嵌套。

所以一个完全异步的函数应该是这样的:

getUserOrders2(@param.path.number('id') id: number): Promise<Array<OrderFrame>> {
  if (!this.user) throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);
  if (this.user.id != id) throw new HttpErrors.Unauthorized(AuthErrorKeys.ClientInvalid);

  return this.restaurantRepository
    .orders(this.user.restaurantId).find().then(
      orders => Promise.all(orders.map(
        order => this.orderRepository.orderItems(order.id).find().then(
          order => new OrderFrame(order)
        )
      ))
    );
}

此函数的 async/await 等价物会更复杂。

然后您将在调用代码中等待结果,因为无论如何您都必须这样做:

async test() {
  const orders = await foo.getUserOrders2(someUserId);
  // ...
}

// or

test() {
  foo.getUserOrders2(someUserId).then(orders => {
    // ...
  });
}