Async Await с четырьмя вложенными циклами - PullRequest
0 голосов
/ 30 сентября 2019

В настоящее время я пытаюсь вернуть массив объектов JSON, который требует от меня выполнения одной асинхронной функции, а затем четырех вложенных асинхронных картографических функций для заполнения массива объектов. По сути, каждый пользователь имеет массив заказов, каждый заказ имеет массив элементов, каждый элемент имеет массив параметров, а каждый параметр имеет массив значений. Я использую loopback4 framework и поэтому не могу сделать res.send после того, как все вещи будут заполнены. Кажется, что функция возвращается в первое ожидание, но любое ожидание после этого она не ожидает и вместо этого запускается до конца функции. Я пытался использовать Promises и .thens (), но не могу понять, как заполнить каждую сущность полностью вложенной, а затем вернуть массив заполненных сущностей. Я продолжаю получать пустой массив. Ниже только одно гнездо карт, но я не могу заставить его даже заполнить первое гнездо и вернуть его, поэтому я решил не идти дальше. Это код:

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. Мне нужно четыре вложенных цикла карты, и этот первый не работает, поэтому я не знаю, как сделать все остальное. Любая помощь будет принята с благодарностью.

На основе решения @Tomalaks я попробовал следующее, но он по-прежнему только возвращает массив верхнего уровня и ничего не вложено:

    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, был правильным. Мне просто нужно было заключить всю функцию в скобки и поставить .hen, чтобы вернуть заполненную сущность, которую я создал

1 Ответ

2 голосов
/ 30 сентября 2019

Вы только должны использовать async, когда вы используете await в той же функции. Если во вложенной функции есть await, родительская функция не нуждается в async.

Однако, в вашем случае, нет функции, которую нужно сделать async во-первых.

  • Нет никакой выгоды в ожидании каких-либо результатов в вашей функции, потому чтоникакой код внутри не зависит от какого-либо промежуточного результата. Просто возвращайте обещания по мере их получения.
  • Нет необходимости в промежуточных переменных результата, таких как orderFrameArray, вы усложняете задачу, ожидая отдельных заказов и подталкивая их к вершине. переменная уровня.
  • Использование await в цикле, как вы делаете в вызове .map(), отрицательно сказывается на производительности. Вы в основном сериализуете доступ к базе данных таким способом - следующий запрос будет отправлен только после того, как текущий будет возвращен. Этот вид последовательного соединения сводит на нет способность базы данных обрабатывать несколько одновременных запросов.
  • 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 => {
    // ...
  });
}
...