J'ai deux méthodes, la première s'abonne à la valeur d'un service qui à son tour reçoit une valeur d'une API back-end. Je dois utiliser cette valeur dans une méthode distincte, donc je dois attendre que la valeur de l'API soit renvoyée par la première méthode.
Le problème est que je ne veux pas transformer toute la méthode async en utilisant le mot-clé await, donc à la place, j'ai pensé que faire en sorte que la première méthode retourne une promesse et ensuite utiliser .then () pour attendre la valeur fonctionnerait. Cependant, lorsque j'exécute mon code, console.log imprime simplement undefined.
async determinePreselectedUsers(googleUsers: GoogleUser[]): Promise<any> {
let emails: string[] = [];
googleUsers.forEach(user => {
emails.push(user.email);
});
await this._usersService.determinePreselectedUsers$(emails).subscribe(res => {
return res;
});
}
onOUSelect(selectedOrganisationalUnitPath: string) {
this._usersService.getGoogleUsers$(selectedOrganisationalUnitPath).subscribe((googleUsers: GoogleUser[]) => {
this.determinePreselectedUsers(googleUsers).then( preselectedUsers => {
console.log("Preselected users: ", preselectedUsers); //This is undefined
});
});
}
Je suppose qu'il existe un moyen de faire fonctionner cela sans utiliser await?
4 Réponses :
Ce n'est pas la première fois que je vois des gens utiliser async / await sur observable et je me demande d'où vient la confusion ou même si c'est moi qui suis confus ici.
Quoi qu'il en soit:
Il y a beaucoup de choses qui ne vont pas dans cette seule ligne:
determinePreselectedUsers(googleUsers: GoogleUser[]): Observable<any> {
let emails: string[] = [];
googleUsers.forEach(user => {
emails.push(user.email);
});
return this._usersService.determinePreselectedUsers$(emails);
}
onOUSelect(selectedOrganisationalUnitPath: string) {
this._usersService.getGoogleUsers$(selectedOrganisationalUnitPath).pipe(
switchMap(googleUsers: GoogleUser[]) => this.determinePreselectedUsers(googleUsers)),
}).subscribe(preselectedUsers => {
console.log("Preselected users: ", preselectedUsers); //This is undefined
});
}
Je suppose que vous espérez attendre que l'abonnement soit résolu et que le res soit déclaré dans la portée.
Vous devriez faire quelque chose comme ça
XXX
Ce qui dans votre extrait serait:
obs1.pipe( switchMap(result1 => fetchSomethingElse(result1)) ).subscribe(result2 => console.log(result2));
Cela a beaucoup plus de sens, merci. Je viens d'avoir une pensée, puisque le service ne renvoie qu'une seule valeur de l'API, si je convertis l'Observable en une promesse en utilisant asPromise (), cela simplifierait-il considérablement la situation dans son ensemble?
suggérerait d'apprendre / d'utiliser des observables, plutôt que de se convertir en promesse
@ Jake12342134 vous pouvez simplifier en mettant tout comme promesse mais je vous conseillerais de choisir l'un ou l'autre et de ne pas mélanger les deux si vous n'êtes pas encore à l'aise avec la différence
FYI cela ne fait rien .... l'observable n'est jamais abonné aussi. Si vous venez de Promises , vous ne comprendrez pas pourquoi cela ne fonctionne pas. Les promesses seront résolues même si then () n'est jamais appelé. C'est donc un bon exemple de la raison pour laquelle vous ne devriez pas simplement toPromise () au lieu d'apprendre comment fonctionnent les observables.
@Reactgular a édité et mis le robinet dans l'abonnement à la place pour moins de confusion: / mais Reactgular a raison, vous devriez apprendre ceci
@Ced Votre code ne fonctionne pas, erreur du compilateur sur la variable googleUsers
determinePreselectedUsers doit renvoyer une valeur à l'aide de la fonction résoudre dans la Promise renvoyée, et non pas renvoyer la valeur directement.
Essayez ceci:
determinePreselectedUsers(googleUsers: GoogleUser[]): Promise<any> {
let emails: string[] = [];
googleUsers.forEach(user => {
emails.push(user.email);
});
return this._usersService.determinePreselectedUsers$(emails).toPromise();
}
ou, Vous pouvez utiliser la fonction toPromise d'Observable, comme:
determinePreselectedUsers(googleUsers: GoogleUser[]): Promise<any> {
return new Promise((resolve, reject) => {
let emails: string[] = [];
googleUsers.forEach(user => {
emails.push(user.email);
});
this._usersService.determinePreselectedUsers$(emails).subscribe(
// return response in then using resolve
resolve,
// return error in catch using reject
reject
);
});
}
Ci-dessous devrait fonctionner
determinePreselectedUsers(googleUsers: GoogleUser[]): Observable<any> {
let emails: string[] = [];
googleUsers.forEach(user => {
emails.push(user.email);
});
return this._usersService.determinePreselectedUsers$(emails);
}
onOUSelect(selectedOrganisationalUnitPath: string) {
this._usersService.getGoogleUsers$(selectedOrganisationalUnitPath).subscribe((googleUsers: GoogleUser[]) => {
this.determinePreselectedUsers(googleUsers).then( preselectedUsers => {
console.log("Preselected users: ", preselectedUsers); //This is undefined
});
});
}
Vous pouvez simplifier votre code source à moins de lignes à l'aide d'un opérateur map et switchMap pour passer à un autre observable. Le principal avantage de l'utilisation de Rxjs est qu'il nous permet d'écrire déclarativement ce que fait la logique. Les promesses sont parfaitement bonnes à utiliser, mais elles décrivent la résolution des données tandis que la programmation réactive décrit comment les données circulent.
function onOUSelect(selectedOrganisationalUnitPath: string) {
const pluckEmails = users => [users, users.map(user => user.email)];
const handleEmails = ([users, emails]) => this._usersService.determinePreselectedUsers$(users, emails);
this._usersService.getGoogleUsers$(selectedOrganisationalUnitPath).pipe(
map(pluckEmails),
switchMap(handleEmails)
).subscribe(values => console.log(values)); // you must subscribe!
}
L'utilisation de async / wait a un côté connu effets, y compris la gestion des erreurs, les difficultés de concurrence et le fait qu'il s'agit d'un sucre de syntaxe pour rédiger des promesses. Lorsque vous utilisez toPromise () avec un observable, vous modifiez intrinsèquement le comportement de la façon dont un observable gère les erreurs et les états vides. Il y a des moments où vous devez l'utiliser, mais revenir à toPromise () pour que vous puissiez continuer à travailler avec des promesses est un anti-modèle dans Angular.
Les observables sont les la première chose que vous devez apprendre dans Angular, et la seule chose que vous ne finirez jamais d'apprendre.
C'est une option beaucoup plus simple, merci. Existe-t-il également un moyen pour moi de pouvoir accéder à l'objet googleUsers d'origine à partir du premier observable? J'ai besoin des deux.
@ Jake12342134 J'ai mis à jour ma réponse afin que la variable googleUsers et les e-mails soient tous deux transférés vers le handleEmails .
Ah je m'excuse, je n'ai pas dû être clair. Je demandais s'il existe un moyen d'accéder aux deux variables dans l'abonnement? Donc avoir des références aux utilisateurs et aux valeurs . Cependant j'imagine puisque le seul abonnement combiné ne peut avoir qu'une seule émission que ce n'est pas possible?
Vous ne pouvez pas utiliser
async / awaitsur une observable.Vous pouvez utiliser quelque chose comme rxjs mergeMap, switchMap pour combiner les observables@c_ogoo Il n'y a qu'une seule observable ici, il n'y a rien avec quoi la combiner? Tout ce que je veux faire, c'est canaliser la valeur d'un observable vers une autre méthode.
Vous devez créer une observable d'ordre supérieur. Les deux observables que vous devez combiner sont:
this._usersService.getGoogleUsers $etthis.determinePreselectedUsersvotre méthodethis.determinePreselectedUsersrenverra l'observable < code> this._usersService.determinePreselectedUsers $