Prenons l'exemple suivant:
cancellable = Just(2).map { x in Just(x * x).delay(for: 2.0, scheduler: RunLoop.main) } .switchToLatest() .sink(receiveCompletion: {_ in print("completed") }, receiveValue: {result in print(result) })
Ici, j'essaie d'imiter le comportement du célèbre opérateur switchMap en utilisant les opérateurs Combine. Je m'attends à obtenir le résultat après deux secondes et l'achèvement. En réalité, ni résultat ni achèvement ne viennent. Ce qui est très mauvais car l'amont a été terminé!
On dirait que switchToLatest s'annule dès que l'amont se termine et oublie de se terminer.
D'un autre côté, si je le remplace par un flatMap
, tout fonctionne comme prévu.
Y a-t-il de bons exemples d'un opérateur switchMap approprié?
Clause de non-responsabilité: Eh bien, je comprends mon achèvement en amont. Bien que je souhaite que mon switchMap fonctionne indépendamment du fait que mon amont soit terminé avant l'éditeur interne ou après.
3 Réponses :
Ne vouliez-vous pas dire pour ce qui suit (du moins, pour autant que je sache, cela aboutit à ce que vous semblez attendre)?
let cancellable = Just(2) .map { x in Just(x * x) } .delay(for: 2.0, scheduler: RunLoop.main) .switchToLatest() .sink(receiveCompletion: {_ in print("completed") }, receiveValue: {result in print(result) })
Désolé, ce n'est pas ce que je voulais dire. Je voulais montrer que l'exemple suivant ne génère pas de sorties et d'événements terminés, ce qui est une erreur en soi. De plus, dans votre exemple, vous avez retardé la sortie d'une carte tandis que j'ai retardé l'observable interne de la carte, ce qui a causé le comportement en question.
Pour rendre les choses encore plus simples: je pense que publisher.map (transform) .switchToLatest ()
devrait générer des événements même si l'éditeur termine avant l'éditeur interne.
Je pense que c'est un bug. Essayez plutôt d'utiliser flatMap
, qui fonctionne comme prévu.
let cancellable = Just(2) .flatMap { (x) in Just(x * x) .delay(for: 2.0, scheduler: RunLoop.main) } .sink(receiveCompletion: {_ in print("completed") }, receiveValue: {result in print(result) })
C'est vrai, sauf que flatMap
n'est pas ce que je veux :-) Je veux un switchMap
en fait. Imaginez que j'ai un flux au lieu de Just ()
et que je souhaite annuler les abonnements précédents lorsqu'il émet. Eh bien, j'ai déjà implémenté un opérateur personnalisé en le faisant donc je le posterai probablement plus tard.
@norekhov s'il vous plaît postez votre solution, j'aimerais en tirer des leçons.
@norekhov btw il n'y a vraiment pas beaucoup de documents disponibles pour être honnête, je suis celui-ci non officiel: heckj.github.io/swiftui-notes/... . D'après ce qu'il dit, il n'y a aucune différence entre .map (). SwitchToLatest ()
et .flatMap ()
. Vous venez peut-être de ReactiveCocoa?
Vous pouvez vérifier ma solution ici: github.com/tcldr/Entwine/issues/16 . Il sera peut-être inclus dans Entwine ou l'auteur suggérera une autre solution de contournement. Ce n'est en fait que switchToLatestWaitable
mais il peut être combiné avec map pour former un switchMap approprié
En effet, switchToLatest
est probablement ce que vous recherchez. Le problème est qu'il est buggé. Ou du moins c'était; le bogue est corrigé dans Xcode 11.4 (actuellement en version bêta) et maintenant switchToLatest
se comporte exactement comme on s'y attendrait. Votre code fonctionne désormais correctement:
Just(2).map { x in Just(x * x).delay(for: 2.0, scheduler: RunLoop.main) } .switchToLatest() .sink(receiveCompletion: {_ in print("completed") }, receiveValue: {result in print(result) }).store(in:&storage) // 4, completed
Lorsque vous dites que cela est corrigé dans Xcode 11.4, cela signifie-t-il que tous les appareils exécutant iOS 13.3 ou une version antérieure seront affectés par ce bogue? Je suppose que la version de Combine qui est livrée avec ces systèmes d'exploitation est boguée, donc que le binaire ait été construit avec Xcode 11.4 ou non ne fait pas de différence, êtes-vous d'accord?
@Rog C'est ce que je pense que cela signifie, oui. Fondamentalement, cette fonctionnalité de Combine n'est devenue utilisable qu'avec iOS 13.4. C'est dommage, mais c'est ce que je pense arrivé. Heureusement, il est extrêmement improbable qu'un utilisateur avec iOS 13 ne se soit pas mis à jour vers iOS 13.4 ou une version ultérieure. - Mais vous pouvez tester cela par vous-même, il n'y a pas besoin de spéculer.
Yuck semble être un autre bug dans Combine. J'ai eu du mal avec une combinaison de
.receive (on: queue) .combineLatest (Just (2))
. Reste silencieux comme dans votre exemple, etflatMap
fonctionne très bien.Merci d'avoir posé cette question; cela m'a juste mordu aussi, donc je suis heureux de découvrir que je ne suis pas seul.
Le bogue est corrigé dans Xcode 11.4