【问题标题】:Vapor 3: Eventloop bug detected when using wait()Vapor 3:使用 wait() 时检测到 Eventloop 错误
【发布时间】:2019-07-20 12:25:52
【问题描述】:

我正在努力理解如何批量保存获取的对象并将它们存储到数据库中。将对象存储到数据库后,我想返回查询结果。 我无法理解如何使用 EventLoopF​​uture 执行此操作,因为当我调用 .wait() 时,我收到错误消息:

前提条件失败:检测到错误:在 EventLoop 上时不得调用 wait()。

作为我的问题的一个例子:

  • 我需要从外部端点获取一个实体(比如说机场的航班)
  • 该调用的结果需要保存到数据库中。如果该航班存在于数据库中,则需要对其进行更新,否则会创建。
  • 完成后,需要返回数据库中所有航班的列表。

这是我目前得到的,但这给了我错误:

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(),因为它会阻塞eventLoop,但是如果我打电话给mapflatMap,我需要反过来返回一个EventLoopFuture&lt;U&gt;UFlight),我完全不感兴趣在。

我希望调用 self.storeOrUpdateFlightRecord 并忽略结果。我该怎么做?

【问题讨论】:

    标签: swift future event-loop vapor vapor-fluent


    【解决方案1】:

    是的,你不能在 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(...),因为闭包不能抛出,但它现在可以工作了!感谢您的帮助。
    猜你喜欢
    • 2021-12-15
    • 2018-10-15
    • 1970-01-01
    • 2018-10-24
    • 1970-01-01
    • 2017-04-06
    • 1970-01-01
    • 2012-04-01
    • 1970-01-01
    相关资源
    最近更新 更多