#swift #combine
#swift #объединить
Вопрос:
У меня следующая ситуация:
2 фьючерса, один возвращает значение, которое меня интересует, другой выполняет некоторые операции и возвращает void. 2 не связаны друг с другом (поэтому код не должен смешиваться), но оба должны выполняться в правильном порядке в логике приложения.
Что я хочу сделать, так это подписаться на издателя, который выполняет следующее:
- будущее выполняется и выдает значение
- future two выполняется и ничего не возвращает
- подписчик получает значение future one после выполнения future two.
Вот небольшой пример кода, который не компилируется, который показывает, чего я хотел бы достичь:
import Combine
func voidFuture() -> Future<Void, Error> {
return Future<Void, Error> { promise in
promise(.success(()))
}
}
func intFuture() -> Future<Int, Error> {
return Future<Int, Error> { promise in
promise(.success(1))
}
}
func combinedFuture() -> AnyPublisher<Int, Error> {
var intValue: Int!
return intFuture().flatMap { result in
intValue = result
return voidFuture()
}.flatMap{ _ in
return CurrentValueSubject(intValue).eraseToAnyPublisher()
}.eraseToAnyPublisher()
}
var subscriptions = Set<AnyCancellable>()
combinedFuture()
.sink(receiveCompletion: { _ in }, receiveValue: { val in print(val)})
.store(in: amp;subscriptions)
Комментарии:
1. Итак, в каком порядке должны выполняться Фьючерсы? Вы хотите
intFuture
выполнить сначала, а затемvoidFuture
или наоборот? Из вашего описания у меня такое чувство, что вы хотитеvoidFuture
выполнить первым. Имейте в виду, что ваши издатели не должны выполнять побочные эффекты (что, у меня такое чувство, что они делают, учитывая тот факт, что вам нужен издатель, который не принимает входных аргументов и не выдает никаких значений для выполнения после другого издателя). Побочные эффекты делают функции нечистыми, а combine — это функциональный реактивный фреймворк, поэтому все издатели должны быть чистыми функциями.
Ответ №1:
Вам нужно, чтобы .map
Void
результат второго publisher ( voidFuture
) вернулся к результату первого publisher ( intFuture
), который .flatMap
будет выдавать:
func combinedFuture() -> AnyPublisher<Int, Error> {
intFuture().flatMap { result in
voidFuture().map { _ in result }
}
.eraseToAnyPublisher()
}
Комментарии:
1. Это работает. Проблема с картой <-> flatMap все еще вызывает у меня головную боль. Все еще трудно понять, когда использовать какой из них.
2. @JorisMans,
.map
оператор принимает переданное значение от вышестоящего издателя и сопоставляет его с другим значением..flatMap
принимает переданное значение и создает издателя, а затем выдает значения этого издателя. В этом случаеflatMap
создается avoidPublisher().map {...}
, который является издателем, который выдаетInt
значение после пустого значения3. Да, это тоже текст, который я запомнил, но его все еще сложно применить на практике. Как и в приведенном выше коде, почему нельзя использовать flatMap для возврата нового издателя, который выдает это значение результата? пример: voidFuture().flatMap { _ в CurrentValueSubject(результат) } или что-то в этом роде.
4. @JorisMans, технически вы можете — Swift просто не может вывести типы, поэтому вам нужно будет более четко указать возвращаемое значение каждого
flatMap
, т..flatMap { _ -> AnyPublisher<Int, Error> in .... }
Е.. Вы также на самом деле не используетеCurrentValueSubject
каким-либо значимым образом — вы можете просто использоватьJust(intValue).setFailureType(to: Error.self)
. Это просто излишне запутанно, поскольку у вас уже есть значение, которое вы хотите вернуть внутри первого.flatMap
, поэтому нет необходимости проходить через маршрут локальной переменной