Дождитесь завершения асинхронной задачи в запросе в Vapor
Я пишу API с использованием серверной части Swift Framework Vapor. Моя модель очень проста. у меня есть Workout
таблица, которая имеет отношение ко многим Circuit
таблица, которая имеет много отношения с Exercise
Таблица. Когда потребитель API создает тренировку, JSON содержит все схемы и упражнения в тренировке. Вот пример:
{
...
circuits: [{
...,
exercises: [{
"title": ...
}, {
"title": ...
}]
}, {
...,
exercises: [{
"title": ...
}, {
"title": ...
}]
}]
}
Проблема, с которой я сталкиваюсь, заключается в создании дублирующих упражнений, когда пользователь выполняет одно и то же упражнение в нескольких цепях. Я запрашиваю базу данных перед созданием нового упражнения, но кажется, что все сохранения происходят асинхронно, что не позволяет мне обнаружить существующее упражнение с тем же идентификатором / заголовком в базе данных перед сохранением нового. Вот мой код:
private func saveExercise(on conn: DatabaseConnectable, with content: WorkoutCreateExerciseContent, for authUser: User) throws -> Future<Exercise> {
if let exerciseId: UUID = content.id {
return Exercise.find(exerciseId, on: conn)
.unwrap(or: Abort(.badRequest, reason: "No exercise for id: \(exerciseId.uuidString)"))
} else if let title: String = content.title, let isPublicallyVisible: Bool = content.isPublicallyVisible {
return Exercise.query(on: conn).filter(\.title == title).filter(\.directions == content.directions).first().flatMap({ (possibleMatchingExercise: Exercise?) -> Future<Exercise> in
if let matchingExercise: Exercise = possibleMatchingExercise {
return conn.future(matchingExercise)
} else {
print("xxx Found no match for \(title)")
let newExercise: Exercise = Exercise(
title: title,
directions: content.directions,
isPublicallyVisible: isPublicallyVisible
)
newExercise.userId = authUser.id
return newExercise.save(on: conn)
}
})
} else {
throw Abort(.badRequest, reason: """
Bad data for exercise. Expecting either of the following:
{
/"id/": /"some id/"
}
or
{
/"title/": /"some title/"
/"directions/": /"(optional)/",
/"isPublicallyVisible/": false
}
id takes priority over other properties.
""")
}
}
private func saveCircuit(on conn: DatabaseConnectable, with content: CircuitCreateContent, order: Int, workoutId: UUID, authUser: User) throws -> Future<Circuit> {
return try content.exercises.map({ (exerciseContent: WorkoutCreateExerciseContent) throws -> Future<Exercise> in
let createdExercise: Future<Exercise> = try self.saveExercise(
on: conn,
with: exerciseContent,
for: authUser
)
return createdExercise
})
.flatten(on: conn)
.flatMap({ (exercises: [Exercise]) -> Future<Circuit> in
let circuit: Circuit = Circuit(
workoutId: workoutId,
order: order,
exerciseDuration: content.exerciseDuration,
restDuration: content.restDuration,
exerciseIDs: exercises.compactMap({ $0.id })
)
return circuit.save(on: conn)
})
}
func saveWorkout(_ conn: DatabaseConnectable, _ authentiatedUser: User, _ content: WorkoutCreateContent) throws -> Future<Workout> {
let workout: Workout = content.makeWorkout()
workout.userId = authentiatedUser.id
// First save the exercises
let createdWorkout: Future<Workout> = workout.save(on: conn).flatMap({ (savedWorkout: Workout) -> Future<Workout> in
let createdCircuits: Future<[Circuit]> = try content.circuits.enumerated().map({ (order: Int, circuitContent: CircuitCreateContent) throws -> Future<Circuit> in
let createdCircuit: Future<Circuit> = try self.saveCircuit(
on: conn, with: circuitContent,
order: order,
workoutId: savedWorkout.id!,
authUser: authentiatedUser
)
return createdCircuit
})
.flatten(on: conn)
return createdCircuits.then({ _ -> Future<Workout> in
return conn.future(savedWorkout)
})
})
return createdWorkout
}
Кто-нибудь видит, что мне здесь не хватает? Есть ли способ ждать Future
выполнить перед началом нового Future
? Я новичок в Vapor, поэтому я не настолько знаком с беглым API Vapor.
0 ответов
Допустим , у нас есть два воображаемых метода, возвращающих Future<>
func randomUInt() -> Future<UInt> { ... }
func multiply(value: UInt, by multiplier: UInt) -> Future<UInt> { ... }
и мы хотим умножить случайное число на 5, но если случайное число равно нулю или 1, мы хотим выдать ошибку. поэтому нам нужно дождаться случайного числа, прежде чем вызыватьmultiply(value: ,by: )
метод.
let result: Future<UInt> = randomUInt().flatMap { (value) -> Future<UInt> in
guard value > 1 else { throw YourError }
return multiply(value: value, by: 5)
}