Si j'ai un observable qui prend beaucoup de temps à exécuter. Nous l'appellerons longObservable
qui est de type Observable, prend 5 secondes pour s'exécuter, et chaque fois qu'il s'exécute, il n'émet une nouvelle chaîne qu'une seule fois puis se termine.
longObservable.subscribe() // Start a new iteration and wait // 5 seconds, get a new string.
Et certaines autres pages l'appellent plusieurs fois. Si c'est en cours, j'aimerais simplement continuer ce processus. S'il est terminé, j'aimerais le redémarrer.
longObservable.subscribe() // Get the same string as // the above subscription in 3 seconds.
et cela s'exécute deux secondes plus tard:
longObservable.subscribe() // Immediate, start the timer
et cela s'exécute 20 secondes plus tard
longObservable(): Subject<string> { return timer(5000).pipe{ map(() => randomString()) } }
Le deuxième abonnement, je pense, est facile, cela fonctionnera comme je le souhaite. C'est le 3ème avec lequel j'ai du mal. Il émettra la même valeur que les deux autres immédiatement puisque longObservable
est terminé.
Ceci est utilisé pour la géolocalisation sur un appareil. Je veux demander un nouvel emplacement, mais s'il y a déjà une demande en cours, utilisez simplement ce résultat.
Modifier: Observable changé en sujet pour la multidiffusion, suppression de take (1).
Edit2: https://stackblitz.com/edit/angular-venpk4 voici un exemple de travail de ce que je veux. J'espère accomplir cela sans la variable timerRunning et avec les opérateurs RxJS. Il se trouve sous le composant hello et s'imprime sur la console.
3 Réponses :
Comme vous le montrez dans votre exemple, votre méthode retourne une nouvelle instance Observable, qui se crée à chaque fois que vous y souscrivez. Je pense que dans votre service, vous pouvez créer une propriété, qui stockera votre observable. Il peut être préférable de définir cette propriété en tant que BehaviorSubject. Et où vous voulez, vous pouvez vous abonner à cette propriété. Ainsi, chaque abonnement sera à la même instance Observable.
Alors, après avoir fait cela, ce que j'ai fait, comment puis-je faire en sorte que le troisième abonnement agisse comme je le souhaite? Il émettra la valeur précédente de la minuterie de 5 secondes. Je veux qu'il démarre une nouvelle minuterie de 5 secondes et obtienne une nouvelle valeur (seulement après la première fois). Je veux toujours que les deux premiers abonnements agissent de la même manière.
Si je vous comprends bien, le 1er et le 2ème abonnement doivent être souscrits au même flux, mais le 3ème doit créer un nouveau flux. Si j'ai raison, la multidiffusion vous aide angular.io/guide/observables#multicasting
La multidiffusion a résolu que mes premier et deuxième abonnements obtiennent la même valeur. Mais comment puis-je le faire réexécuter le minuteur uniquement si le minuteur en cours d'exécution est terminé pour mon troisième exemple d'abonnement? Merci pour ton aide au passage.
Je suppose que pour être plus clair, j'obtiens l'emplacement de l'utilisateur. S'il y a déjà une demande pour obtenir son emplacement, je veux qu'elle renvoie simplement ce résultat. Mais s'il est déjà sorti et terminé, je souhaite récupérer à nouveau l'emplacement de l'utilisateur.
Pourriez-vous montrer un exemple sur stackblitz.com ? Il sera beaucoup plus facile de comprendre votre question.
Après avoir lu vos attentes - "J'espère accomplir cela sans la variable timerRunning et avec les opérateurs RxJS." - Je pense qu'un exemple de multidiffusion dans angular.io/guide/observables#multicasting convient pour toi. Je veux dire que vous devriez utiliser un champ qui stocke votre état
Yah c'est ce que j'ai fini par faire. J'espérais éviter cela, je ne sais pas pourquoi.
Problème épineux. Voici ma solution dans un StackBlitz . L'opérateur share ()
, qui transforme effectivement l'observable en sujet, sans avoir à déclarer le sujet explicitement, en est une des clés. Cependant, vous avez besoin d'un NOUVEAU sujet à créer avec un nouvel abonnement une fois l'ancien terminé, j'ai donc créé une fonction d'usine pour soit retourner l'Observable partageable existant (si longObservable ()
est toujours en cours) ou bien en générer un nouveau.
Voici les éléments importants du StackBlitz:
let inProgress: boolean = false; function longObservable(): Observable<string> { return timer(5000).pipe( map(() => randomString()), tap(() => inProgress = false), share() ) } let obs$: Observable<string>; function getLongObs(): Observable<string> { if (inProgress) { return obs$ } else { inProgress = true; obs$ = longObservable(); return obs$; } } console.log('initiate first subscribe'); getLongObs().subscribe( rand => console.log(`First subscribe returned ${rand}`) ); setTimeout(() => { console.log('initiate second subscribe'); getLongObs().subscribe( rand => console.log(`Second subscribe returned ${rand}`) ); }, 2000); setTimeout(() => { console.log('initiate third subscribe'); getLongObs().subscribe( rand => console.log(`Third subscribe returned ${rand}`) ); }, 7000)
J'espère que cela vous aidera!
J'ai considérablement simplifié cela dans le StackBlitz. Cela a été très similaire à ce que @Kyle avait quand tout a été dit et fait. :)
Merci, c'est ce dont j'avais besoin. J'ai fini par utiliser une variable publique pour suivre les états, mais cela aurait fait le travail.
Heureux que cela ait aidé! :)
Je pense que ce que vous voulez, c'est le tube share ()
. Quelque chose comme ça fonctionne:
Timer Triggered! randomString1 randomString1 Timer Triggered! randomString2
Sortie:
export class AppComponent { private _longObservable: Observable<string> = null constructor() { this._longObservable = timer(5000).pipe( // This will show us when timer emits a value which will prove that the // first two subscriptions below are sharing the same "execution" // of the observable. tap(() => console.log("Timer Triggered!")), map(() => randomString()), share() ); } ngOnInit() { // These two will share the observable, // since long observable hasn't completed by the time the second // subscription is triggered. this._longObservable.subscribe(console.log); setTimeout(() => this._longObservable.subscribe(console.log), 2000); // This subscription occurs after the 5 sec. // Since timer is a cold observable, this will trigger it to run again. setTimeout(() => this._longObservable.subscribe(console.log), 7000); } }
Voici un article sur la différence entre les observables chauds et froids si vous ne l'êtes pas familier avec cette distinction: https://medium.com/@benlesh/hot -vs-cold-observables-f8094ed53339
Les requêtes HTTP en angular, et timer (5000)
sont toutes deux des observables froides.
Voici un lien vers des informations sur le canal de partage: https://www.learnrxjs.io/ operators / multicast / share.html
Votre question semble déroutante
remarque:
take (1)
dans votre exemple n'est pas nécessaire