Home > Enterprise >  Vapor 4 - Approaching an issue - Return type of a function
Vapor 4 - Approaching an issue - Return type of a function

Time:08-08

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
}
  • Related