Swift Combine возвращает результат первого будущего после оценки второго

#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 создается a voidPublisher().map {...} , который является издателем, который выдает Int значение после пустого значения

3. Да, это тоже текст, который я запомнил, но его все еще сложно применить на практике. Как и в приведенном выше коде, почему нельзя использовать flatMap для возврата нового издателя, который выдает это значение результата? пример: voidFuture().flatMap { _ в CurrentValueSubject(результат) } или что-то в этом роде.

4. @JorisMans, технически вы можете — Swift просто не может вывести типы, поэтому вам нужно будет более четко указать возвращаемое значение каждого flatMap , т. .flatMap { _ -> AnyPublisher<Int, Error> in .... } Е.. Вы также на самом деле не используете CurrentValueSubject каким-либо значимым образом — вы можете просто использовать Just(intValue).setFailureType(to: Error.self) . Это просто излишне запутанно, поскольку у вас уже есть значение, которое вы хотите вернуть внутри первого .flatMap , поэтому нет необходимости проходить через маршрут локальной переменной