У меня есть сценарий, в котором пользователь может попытаться обновить приложение из нескольких мест, но я хочу обновить его только один раз для каждого запроса.
Просто хотел знать, может ли что-то подобное работать: каждый раз, когда происходит второе обновление или третий запрос, приложение будет ждать текущего выполняемого запроса.
class Test {
private var currentTask: Task<Int, Never>?
@MainActor
func refresh() async -> Result<Int, Never> {
if let currentTask {
return await currentTask.result
}
let task = Task {
// Long operation
return 1
}
currentTask = task
let result = await task.result
currentTask = nil
return result
}
}
@loremipsum – Он этого ждет, не так ли?
Однако я бы, как минимум, отказался от типа Result<Int, Never>, поскольку это ненужный шум. И currentTask тоже не разворачивается.
Да, это ошибка, так и должно быть if let currentTask { Всем спасибо





Да, этот подход будет работать. Я использую что-то очень похожее в своем приложении.
actor Service {
private var task: Task<Void, Error>?
func fetchData() async throws {
if let task {
return try await task.value
}
let task = Task {
try await actualFetchData()
}
self.task = task
defer {
self.task = nil
}
return try await task.value
}
private func actualFetchData() async throws {
// Fetching data from the server/database
}
}
По сути, вы предоставляете метод-оболочку, который гарантирует выполнение запроса на выборку, только если другой не выполняется. Ваша реализация обязательно должна работать.
Эта функциональность может быть расширена для поддержки выборки дискретных данных для конкретного объекта, к которому вы будете обращаться по его уникальному идентификатору.
actor Service {
private var tasks = [UUID: Task<Void, Error>]()
func fetchData(for uuid: UUID) async throws {
if let task = tasks[uuid] {
return try await task.value
}
let task = Task {
try await actualFetchData(for: uuid)
}
tasks[uuid] = task
defer {
tasks[uuid] = nil
}
return try await task.value
}
private func actualFetchData(for uuid: UUID) async throws {
// Fetching data from the server/database
}
}
Спасибо за отсрочку. Я заметил одну вещь: я использую главного актера для защиты задачи от любых проблем с параллелизмом, но вы ничего не использовали. Есть ли за этим какая-то причина?
И да, и нет, мой сервис не должен возвращаться/работать в основной очереди. Если вам нужна такая функциональность, обязательно добавьте @MainActor в сигнатуру функции.
@Роб, спасибо, что указал на это. В этом примере вызов actualFetchData Sure вызывает ошибку. Отметка обеих функций @MainActor исправляет это. Но весь смысл сохранения задачи здесь заключается в том, чтобы гарантировать, что actualFetchData не будет вызываться в одно и то же время (из любого места). Я бы порекомендовал сделать тип Service актером. (что я только что сделал в посте)
Да, актер должен выполнить эту работу, чтобы избежать проблем с параллелизмом и получить текущую задачу. Спасибо.
Нет, вам нужно дождаться внутренней задачи