LeadKit/docs/tifoundationutils/asyncoperation.md

4.4 KiB
Raw Blame History

AsyncOperation<Result, Error> - generic сабкласс Operation

Позволяет запускать:

  • асинхронный код внутри операции
  • собирать цепочки из операций
  • подписываться на результат выполнения

Базовые операции

"Из коробки", на данный момент, доступен всего один сабкласс асинхронной операции, потому что больше обычно и не нужно. Но можно наследоваться и создавать собственные сабклассы при необходимости.

ClosureAsyncOperation<Result, Error>

Операция принимающая некий closure, который по окончании своей работы вызовет completion, переданный ему параметром

import Foundation
import TIFoundationUtils

let intResultOperation = ClosureAsyncOperation<Int, Never> { completion in
    DispatchQueue.global().asyncAfter(deadline: .now() + .seconds(3)) {
        completion(.success(1))
    }
    return Cancellables.nonCancellable()
}

Базовые операторы

На данный момент реализовано четыре оператора:

  • map(mapOutput:mapFailure:) - конвертирует ResultType в новый NewResultType и ErrorType в новый NewErrorType
  • observe(onSuccess:onFailure) - просто вызывает переданные callback'и при получении результата или ошибки
  • flatMap<NewOutput>(_:) - подписывается на результат выполнения нового AsyncOperation полученного из closure
  • flatMapError<NewFailure>(_:) - подписывается на результат выполнения нового AsyncOperation полученного из closure

Пример запуска асинхронных операци с применением операторов в последовательной очереди и вывод результатов

let operationQueue = OperationQueue()
operationQueue.maxConcurrentOperationCount = 1

ClosureAsyncOperation<Int, Never> { completion in
    DispatchQueue.global().asyncAfter(deadline: .now() + .seconds(3)) {
        completion(.success(1))
    }
    return Cancellables.nonCancellable()
}
.map { $0 * 2 }
.observe(onSuccess: { result in
    debugPrint("Async operation one has finished with \(result)")
})
.add(to: operationQueue)

ClosureAsyncOperation<String, Never> { completion in
    DispatchQueue.global().asyncAfter(deadline: .now() + .seconds(1)) {
        completion(.success("Success"))
    }
    return Cancellables.nonCancellable()
}
.observe(onSuccess: { result in
    debugPrint("Async operation two has finished with \(result)")
})
.add(to: operationQueue)

В консоли будет выведено:

"Async operation one has finished with 2"
"Async operation two has finished with Success"

Пример использования оператора flatMap у AsyncOperaton

import TISwiftUtils

extension StorageKey {
    static var loyaltyCardNumber: StorageKey<String> {
        .init(rawValue: "loyaltyCardNumber")
    }
}

struct CardService {
    enum Failure: Error {
        case noCardFound
        case cardFetchError
    }

    private let operationQueue = OperationQueue()

    private let asyncStorage = StringValueDefaultsStorage(defaults: .standard, storageKey: .loyaltyCardNumber)
        .async(on: .global())

    func requestCardTitle(cardNumber: String) -> AsyncOperation<String, Failure> {
        .just(success: "Supreme card")
//        .just(failure: .cardFetchError)
    }

    func getSavedCardTitle(completion: @escaping UIParameterClosure<Result<String, Failure>>) -> Cancellable {
        ClosureAsyncOperation { completion in
            asyncStorage.getValue {
                completion($0.mapError { _ in
                    .noCardFound
                })
            }
        }
        .flatMap {
            requestCardTitle(cardNumber: $0)
        }
        .observe(onResult: completion)
        .add(to: operationQueue)
    }
}

let cardService = CardService()

cardService.getSavedCardTitle { result in
    debugPrint(result)
}

Nef.Playground.needsIndefiniteExecution(true)