具有四个嵌套循环的异步等待
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 => {
// ...
});
}
我目前正在尝试 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 => {
// ...
});
}