Vapor 4中如何从EventLoopFuture<String>类型中提取字符串值
核心处理原则
你无法同步从EventLoopFuture中取出值,所有依赖Future结果的操作都必须放在Future的异步回调链中(map/flatMap等方法的闭包内),等待异步操作完成后再执行。你的问题本质是当前的表格构建逻辑是同步执行的,但分类名称的查询是异步操作,需要调整数据加载顺序,先拿到所有分类名称再构建表格。
最优解决方案:关联预加载(推荐)
Fluent支持预加载关联模型,只需2次SQL查询即可拿到所有帖子和对应的分类数据,避免循环查询的N+1性能问题,是该场景的首选方案。
- 首先确认你的
BlogPostModel已经定义了指向BlogCategoryModel的Parent关联:
final class BlogPostModel: Model { // 其他字段定义省略 @Parent(key: "category_id") var category: BlogCategoryModel }
- 在
BlogPostAdminController中重写默认的listView方法,查询帖子时预加载关联分类:
struct BlogPostAdminController: ListController{ typealias Model = BlogPostModel // 重写默认实现,增加关联预加载逻辑 func listView(req: Request) throws -> EventLoopFuture<View> { BlogPostModel.query(on: req.db) // 预加载所有帖子对应的分类数据 .with(\.$category) .all() .map { posts in self.listTable(posts, req: req) } .flatMap { table in req.view.render("Common/List", listContext(req: req, table: table)) } } func listTable(_ models: [Model], req: Request) -> Table { Table(columns: ["image", "title", "category"], rows: models.map { model in TableRow(id: model.id!.uuidString, cells: [ TableCell(type: .image, model.image), TableCell(model.title), // 直接取预加载完成的分类名称,无需异步查询 TableCell(model.category.title) ]) }) } // 原来的getCategory方法可以删除了 }
备选方案:手动批量等待异步查询结果
如果你实在无法使用关联预加载,可以用EventLoop.traverse批量等待所有分类查询完成后再构建表格,注意该方案会产生N+1次SQL查询,性能较低:
struct BlogPostAdminController: ListController{ typealias Model = BlogPostModel func listView(req: Request) throws -> EventLoopFuture<View> { Model.query(on: req.db) .all() .flatMap { posts -> EventLoopFuture<[(post: BlogPostModel, categoryName: String)]> in // 批量执行所有分类查询,等待全部完成后返回组合结果 req.eventLoop.traverse(posts) { post in self.getCategory(from: post.$category.id, andRequest: req) .map { (post: post, categoryName: $0) } } } .map { postWithCategoryList in // 所有分类查询完成后再构建表格 Table(columns: ["image", "title", "category"], rows: postWithCategoryList.map { item in TableRow(id: item.post.id!.uuidString, cells: [ TableCell(type: .image, item.post.image), TableCell(item.post.title), TableCell(item.categoryName) ]) }) } .flatMap { table in req.view.render("Common/List", listContext(req: req, table: table)) } } private func getCategory(from id: UUID, andRequest req: Request) -> EventLoopFuture<String> { BlogCategoryModel.find(id, on: req.db).map { $0?.title ?? "Unknown" } } }
内容的提问来源于stack exchange,提问作者Marcelo Perretta
相关产品推荐
相关产品推荐

