Vapor 3:在使用wait()时检测到Eventloop bug。

4
我对如何批量保存获取的对象并将其存储到数据库感到困惑。在将对象存储到数据库后,我希望返回一个查询结果。使用EventLoopFuture时,我无法理解如何操作,因为当我调用.wait()时,会收到以下错误信息:
Precondition failed: BUG DETECTED: wait() must not be called when on an EventLoop.

以下是我的问题示例:
  • 我需要从外部端点(例如机场航班)中获取实体
  • 需要将调用结果保存到数据库中。如果航班存在于数据库中,则需要更新;否则创建。
  • 完成后,需要返回数据库中所有航班的列表。
以下是我迄今为止的做法,但它给出了一个错误提示:
func flights(on conn: DatabaseConnectable, customerName: String, flightType: FlightType) throws -> Future<[Flight]> {

    return Airport.query(on: conn).filter(\.customerName == customerName).first().flatMap(to: [Flight].self) { airport in
      guard let airport = airport else {
        throw Abort(.notFound)
      }

      guard let airportId = airport.id else {
        throw Abort(.internalServerError)
      }

      // Update items for customer
      let fetcher: AirportManaging?

      switch customerName.lowercased() {
      case "coolCustomer":
        fetcher = StoreOneFetcher()
      default:
        fetcher = nil
        debugPrint("Unhandled customer to fetch from!")
        // Do nothing
      }

      let completion = Flight.query(on: conn).filter(\.airportId == airportId).filter(\.flightType == flightType).all

      guard let flightFetcher = fetcher else { // No customer fetcher to get from, but still return whats in the DB
        return completion()
      }

      return try flightFetcher.fetchDataForAirport(customerName, on: conn).then({ (flights) -> EventLoopFuture<[Flight]> in
        flights.forEach { flight in
          _ = try? self.storeOrUpdateFlightRecord(flight, airport: airport, on: conn).wait()
        }
        return completion()
      })
    }
  }

  func storeOrUpdateFlightRecord(_ flight: FetcherFlight, airport: Airport, on conn: DatabaseConnectable) throws -> EventLoopFuture<Flight> {
    guard let airportId = airport.id else {
      throw Abort(.internalServerError)
    }

    return Flight.query(on: conn).filter(\.itemName == flight.itemName).filter(\.airportId == airportId).filter(\.flightType == flight.type).all().flatMap(to: Flight.self) { flights in
      if let firstFlight = flights.first {
        debugPrint("Found flight in database, updating...")
        return flight.toFlight(forAirport: airport).save(on: conn)
      }

      debugPrint("Did not find flight, saving new...")
      return flight.toFlight(forAirport: airport).save(on: conn)
    }
  }

所以问题出现在此行_ = try? self.storeOrUpdateFlightRecord(flight, airport: airport, on: conn).wait()。我不能调用wait(),因为它会阻塞事件循环,但如果我调用mapflatMap,反过来需要返回一个EventLoopFuture<U>(其中UFlight),而我完全不感兴趣。
我希望调用self.storeOrUpdateFlightRecord并忽略结果。我该怎么做?
1个回答

8

是的,在eventLoop上无法使用.wait()

在您的情况下,您可以使用flatten进行批处理操作。

/// Flatten works on array of Future<Void>
return flights.map {
    try self.storeOrUpdateFlightRecord($0, airport: airport, on: conn)
        /// so transform a result of a future to Void
        .transform(to: ())
}
/// then run flatten, it will return Future<Void> as well
.flatten(on: conn).flatMap {
    /// then do what you want :)
    return completion()
}

太棒了。不得不将它更改为compactMaptry? self.storeOrUpdateFlightRecord(...),因为闭包不能抛出异常,但现在它可以工作了!感谢您的帮助。 - Paul Peelen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接