I have a function that triggers when I hit a certain endpoint, that contains some sort of a condition inside
if room exists {
return room
} else {
create a new room and return it
}
The issue is, that sometimes I return an EventLoopFuture (if the room doesn't exist), and sometimes I return a Room type without an EventLoopFuture (if a room exists), so I am not quite sure what the return type of the function should be.
Now the way I fix it is with a hacky workaround, where I always return an EventLoopFuture, and if the room exists, I update it and return it, which is totally unnecessary as there's nothing to update.
Would the right way be to save the newly created room and return it separately, so the condition will always return a Room type and not an EventLoopFuture?
The original function:
func findEmpty(req:Request) throws -> EventLoopFuture<Room> {
guard let category = req.parameters.get("category") else { throw Abort(.badRequest) }
try checkIfAllowed(category: category)
return Room.query(on: req.db)
.filter(\.$category == category)
.all()
.flatMap { relevantRooms in
for room in relevantRooms {
for isOccupied in room.availablePositions {
if !isOccupied {
return room.update(on: req.db)
.map {
return room
}
}
}
}
let newRoom = Room(...//initializing room)
return newRoom.save(on: req.db)
.map {
return newRoom
}
}
}
CodePudding user response:
With the introduction of async
, you can choose to return either the Future
or the resolved value. However, in most cases, the async
pattern results in simpler code. If you do want to return a Future
of your room
, then it should be as simple as replacing your existing return room
with return req.eventLoop.makeSucceededFuture(room)
instead of the unnecessary update
. so:
return room.update(on: req.db).map {
return room
}
Becomes:
return req.eventLoop.makeSucceededFuture(room)
Converting code using Future
pattern to async
is relatively straightforward (and if it isn't that's probably a sign you should leave it as Future
). In your case:
func findEmpty(req:Request) async throws -> Room {
guard let category = req.parameters.get("category") else { throw Abort(.badRequest) }
try checkIfAllowed(category: category)
let relevantRooms = try await Room.query(on: req.db)
.filter(\.$category == category)
.all()
for room in relevantRooms {
for isOccupied in room.availablePositions {
if !isOccupied {
return room
}
}
}
let newRoom = Room(...//initializing room)
try await newRoom.save(on: req.db)
return newRoom
}
Just an observation, but your inner for
could be replaced with something simpler:
if let unoccupied = room.availablePositions.first { !$0.isOccupied } {
return unoccupied
}