Comment puis-je "attendre" sur un observable Rx?

107

J'aimerais pouvoir attendre sur un observable, par exemple

const source = Rx.Observable.create(/* ... */)
//...
await source;

Une tentative naïve entraîne la résolution immédiate de l'attente et non le blocage de l'exécution

Edit: Le pseudocode pour mon cas d'utilisation prévu complet est:

if (condition) {
  await observable;
}
// a bunch of other code

Je comprends que je peux déplacer l'autre code dans une autre fonction distincte et le passer dans le rappel d'abonnement, mais j'espère pouvoir éviter cela.

Steaks bon marché
la source
Ne pouvez-vous pas déplacer le code restant (dont vous souhaitez attendre la source) dans un .subscribe()appel de méthode?
StriplingWarrior

Réponses:

133

Vous devez faire une promesse await. Convertissez le prochain événement de l'observable en promesse et attendez cela.

if (condition) {
  await observable.first().toPromise();
}

Modifier la note: Cette réponse utilisait à l'origine .take (1) mais a été modifiée pour utiliser .first (), ce qui évite que la promesse ne se résolve jamais si le flux se termine avant qu'une valeur ne passe.

Macil
la source
3
Au lieu de prendre (1) pourriez-vous utiliser await observable.first().toPromise();?
apricity
14
@apricity S'il n'y avait aucune valeur à la fin, first()entraînera le rejet et take(1)entraînera une promesse en attente.
Estus Flask
6
@apricity @AgentME En fait, vous ne devriez PAS utiliser ni take(1)ni first()dans des cas comme celui-ci. Puisque vous vous attendez à ce qu'exactement UN événement se produise, vous devez utiliser single()qui lèvera une exception s'il y en a plus de 1, sans lever d'exception lorsqu'il n'y en a pas. S'il y en a plus d'un, il y a probablement quelque chose qui ne va pas dans votre code / modèle de données, etc. Si vous n'utilisez pas de single, vous finirez par choisir arbitrairement le premier élément qui retourne sans vous avertir qu'il y en a plus. Vous devrez faire attention dans votre prédicat sur la source de données en amont pour toujours maintenir le même ordre.
ntziolis
3
N'oubliez pas l'importation:import 'rxjs/add/operator/first';
Stephanie le
7
Maintenant que toPromise () est obsolète, comment devrions-nous faire cela?
Jus10
26

Cela doit probablement être

await observable.first().toPromise();

Comme il a été noté dans les commentaires précédents, il existe une différence substantielle entre les opérateurs take(1)et first()lorsqu'il y a un observable rempli vide.

Observable.empty().first().toPromise()entraînera un rejet avec EmptyErrorqui peut être géré en conséquence, car il n'y avait vraiment aucune valeur.

Et Observable.empty().take(1).toPromise()se traduira par une résolution avec de la undefinedvaleur.

Fiole d'Estus
la source
En fait take(1), ne donnera pas une promesse en suspens. Cela donnera une promesse résolue avec undefined.
Johan t Hart
Merci d'avoir remarqué, c'est correct. Je ne sais pas pourquoi le message différait, peut-être que le comportement a changé à un moment donné.
Estus Flask
8

Vous aurez besoin d' awaitune promesse, vous voudrez donc l'utiliser toPromise(). Voir ceci pour plus de détails sur toPromise().

Josh Durham
la source
4

Si toPromiseest obsolète pour vous, vous pouvez l'utiliser, .pipe(take(1)).toPromisemais comme vous pouvez le voir ici, il n'est pas obsolète.

Alors s'il vous plaît utilisez juste toPromise(RxJs 6) comme dit:

//return basic observable
const sample = val => Rx.Observable.of(val).delay(5000);
//convert basic observable to promise
const example = sample('First Example')
  .toPromise()
  //output: 'First Example'
  .then(result => {
    console.log('From Promise:', result);
  });

exemple async / await:

//return basic observable
const sample = val => Rx.Observable.of(val).delay(5000);
//convert basic observable to promise
const example = await sample('First Example').toPromise()
// output: 'First Example'
console.log('From Promise:', result);

En savoir plus ici .

Et veuillez supprimer cette fausse revendication disant qu'elle toPromiseest obsolète.

Emerica
la source