2017-05-23 4 views
1

L'exemple de code suivant a l'odeur de (Rx) Swift, mais la question est générique pour n'importe quel langage avec des caractéristiques réactives et une capacité de lancer. Considérons une fonction qui renvoie une séquence observable, mais effectue une vérification de cohérence avant de créer la séquence. Vérifier l'échec signifie que la séquence ne peut pas produire de valeurs.Réactivité: lancer ou publier l'erreur

func yieldFoos() -> Observable<Foo> { 
    guard isValid(internalFoo) else { 
    // throw or return one shot observable? 
    } 
    return createValidObservable(from: internalFoo) 
} 

En cas d'échec de la vérification de la validité de l'Etat, si la fonction lancer ou retourner un coup observable, qui produira jamais juste une erreur? Lancer des avantages et inconvénients:

Le lancement est logiquement plus propre (c'est une défaillance empêchant la création observable), mais entraîne un encombrement du code d'appel - bloc catch, plusieurs points de gestion des erreurs dans différentes portées d'exécution.

Un coup observable se traduit par un code d'appel plus court et plus propre, mais d'une manière ou d'une autre se sent mal. L'observable est forcé d'être un transporteur pour l'état d'erreur non séquentielle, par souci de brièveté.

Quelqu'un ayant une forte opinion mérite-t-il d'être suivi? Ou une autre solution négligée, élégante?

Répondre

2

Je me demande au sujet de votre sentiment qu'il est mauvais pour un Observable à émettre une erreur. Cela fait partie de son travail.

Lorsque vous y pensez, votre fonction createValidObservable(from:) peut émettre une erreur malgré la remise d'un internalFoo valide, de sorte que le code appelant yieldFoos() doit quand même être préparé pour gérer une erreur émise. Vous pourriez aussi bien rouler tout votre code de gestion des erreurs ensemble. J'irais plus loin et rendrais votre fonction de création capable de gérer elle-même des foos invalides en émettant une erreur et en supprimant cette fonction de yieldFoos.

Maintenant, si vous voulez yieldFoos() retourner un Driver plutôt que d'une observable, alors vous devez gérer l'erreur par des jets ou une condition préalable (parce que les conducteurs n'émettent pas d'erreurs.)

func yieldFoos() -> Observable<Foo> { 
    guard isValid(internalFoo) else { 
     return Observable.error(myError) 
    } 
    return createValidObservable(from: internalFoo) 
} 

Je pense que vous devez surmonter votre sentiment que d'avoir un Observable qui renvoie immédiatement une Erreur est faux. C'est une chose parfaitement valable pour un observable à faire et quelque chose que tout le code qui utilise Observable doit être prêt à gérer.

+0

Il n'est pas faux qu'Observable génère une erreur. Je pense juste qu'une telle erreur devrait être "de l'observable" (incapacité à continuer la séquence), pas de créer l'observable en premier lieu. L'échec de la séquence est normal, l'échec de la création est une incohérence exceptionnelle, donc le lancement semble approprié. Mais l'existence d'un seul opérateur 'Observable.error' laisse entendre que je pense trop. –

+0

Si votre fonction create a reçu un internalFoo invalide, il serait incapable de continuer la séquence ... :-) Il peut être utile de considérer l'erreur Observable comme leur version de throw, ou de la considérer comme un throw pour les évènements asynchrones .Cela fonctionne comme un lancer dans le fait qu'il se propage à travers le tuyau jusqu'à ce qu'il soit manipulé ... –

+0

alors voulez-vous le dire de cette façon - dans un code réactif, il y a moins de cas d'utilisation pour lancer en général? Je pense que l'un des concepts réactifs clés est que tout pourrait être progressivement asynchrone et même paresseux, mais votre code de transformation ne devrait pas s'en soucier. Alors que le lancer est intrinsèquement synchrone ... –

1

Votre fonction doit être quelque chose comme ceci:

func yieldFoos() -> Observable<Foo> { 
    Observable.create { observer in 

     guard isValid(internalFoo) else { 
      observer.onError(yourError) 
     } 

     let subscription = 
      createValidObservable(from: internalFoo) 
       .subscribe(onNext: { foo in 
        observer.onNext(foo) 
        observer.onCompleted() 
       }) 
     return Disposables.create { 
      // your dispose 
      subscription.dispose() 
     } 
    } 

} 

Et puis, quand vous l'appelez:

yieldFoos() 
    .subscribe(
    onNext: { foo in 
     // your code with foo 
    }, 
    onError: { error in 
     // manage errors 
    }) 
    .addDisposableTo(disposeBag) 
+1

Je sais construire et utiliser des observables, merci. Je demande si des échecs exceptionnels doivent être jetés ou mis en séquence. Vous semblez favoriser les séquences, mais sans raisonner. –