Преобразовать код с помощью GCD в асинхронный/ожидающий в Swift?

Примерный пример кода, который мне нужно получить с помощью GCD:

func performTask(completion: (() -> ())?) {
    doSomeAsyncTask { [weak self] isSuccess in
        if isSuccess {
            self?.handleResult(completion: completion)
        } else {
            completion?()
            self?.handleResult(completion: nil)
        }
    }
}

func handleResult(completion: (() -> ())?) {
     …
}

func doSomeAsyncTask(completion: ((Bool) -> ())?) {
    //example of async task which can be successful or not
    DispatchQueue.main.async {
        completion?(Bool.random())
    }
}

Я хочу переписать его с помощью async/await, но не знаю, как реализовать performTask. Другие методы:

func handleResult() async {
    …
}

func doSomeAsyncTask() async -> Bool {
    await withCheckedContinuation { checkedContinuation in
        Task {
            checkedContinuation.resume(returning: Bool.random())
        }
    }
}

Не могли бы вы объяснить, как реализовать performTask? Я не могу понять, как быть с ситуацией, когда иногда метод должен вызывать завершение, а иногда нет.

Посмотрите «Meet async/await». Apple проходит весь процесс преобразования. Кроме того, эта плавающая задача не является обязательной и обычно является причиной утечки.

lorem ipsum 27.02.2024 17:33

Несвязанное наблюдение: любопытно, что doSomeAsyncTask выполняет некоторую работу, но не возвращает никакого результата (кроме логического значения) и что handleResult не передает результат для обработки. Я предполагаю, что фактический результат хранится в каком-то свойстве. Обычно предпочтительнее, чтобы doSomeAsyncTask фактически возвращал результаты напрямую (и либо выдавал ошибку, либо возвращал nil в случае сбоя), а затем по мере необходимости предоставлял это значение в качестве параметра handleResult. Здесь недостаточно подробностей (и это выходит за рамки вопроса), это просто к вашему сведению.

Rob 27.02.2024 20:01

@Rob в моем конкретном случае doSomeAsyncTask загружает объявление, handleResult перезагружает объявление с некоторыми проверками и интервалами времени, completion обычно показывает это объявление. Эта внутренняя логика означает «загрузить объявление. Если оно загружено, покажите его и продолжите поток после закрытия объявления. Если есть ошибка, продолжайте поток, но попробуйте предварительно загрузить объявление (возможно, в следующий раз оно будет готово).

Gargo 28.02.2024 08:13

Если это проблема бизнеса, я был бы склонен использовать совсем другой подход, а именно асинхронную последовательность, например, AsyncTimerSequence, которая через некоторый интервал переходит к следующему объявлению.

Rob 28.02.2024 17:18
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
186
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В вашей версии обработчика завершения, если doSomeAsyncTask возвращает false, оно немедленно вызывает замыкание, а затем вызывает handleResult с nil для обработчика завершения. Это очень любопытная закономерность. Единственное, что я могу догадаться, это то, что намерение состоит в том, чтобы немедленно вызвать обработчик завершения, если doSomeAsyncTask не удалось, но все равно вызвать handleResult независимо, но в случае успеха не возвращаться, пока handleResult не будет выполнено.

Если это ваше намерение, параллельная обработка Swift может быть такой:

@discardableResult
func performTask() async -> Bool {
    let isSuccess = await doSomeAsyncTask()
    if isSuccess {
        await handleResult()
    } else {
        Task { await handleResult() }
    }
    return isSuccess
}

Обратите внимание: я возвращаю успех или неудачу doSomeAsyncTask, потому что, как правило, вы всегда хотите, чтобы вызывающая сторона имела возможность узнать, удалось ли это или нет (даже если вас это сейчас не волнует). Итак, я поставил @discardableResult на случай, если вызывающему абоненту в данный момент все равно, удалось ли это или нет.

Другой шаблон — выдать ошибку в случае неудачи (и вызывающая сторона может try?, если ее не волнует успех или неудача на данном этапе).

enum ProcessError: Error {
    case failed
}

func performTask() async throws {
    if await doSomeAsyncTask() {
        await handleResult()
    } else {
        Task { await handleResult() }
        throw ProcessError.failed
    }
}

Показав дословный перевод предоставленного вами кода, я мог бы предложить более простой шаблон:

@discardableResult
func performTask() async -> Bool {
    let isSuccess = await doSomeAsyncTask()
    await handleResult()
    return isSuccess
}

В первых двух альтернативах, описанных выше, я использую неструктурированный параллелизм для обработки сценария, в котором doSomeAsyncTask возвращает false, и разрешаю функции немедленно возвращать результат, а затем выполнять handleResult асинхронно позже. Это (как и воспроизведение обработчика завершения) вызывает вопрос, как обрабатывать отмену. И вопрос в том, достаточно ли медленный handleResult, чтобы оправдать эту закономерность, или это был случай преждевременной оптимизации. Кажется чрезвычайно странным, что можно ожидать handleResult на пути успеха, а не на пути неудач. Третий и последний пример упрощает эту логику. Мы недостаточно знаем о причинах исходной версии обработчика завершения, чтобы ответить на этот вопрос на данный момент.

Другие вопросы по теме