是否有可能在 angular 等待订阅?
Is there a possibility to await a subscription in angular?
我想显示 post 的列表,如下所示:
Post List
为了显示哪个 post 受到用户的青睐,我需要来自 mongodb 数据库中两个不同 collections 的数据。
目前我的 post-list.component.ts 文件的 ngOnInit 看起来像这样:
ngOnInit() {
this.isLoading = true;
this.postsService.getPosts(this.postsPerPage, this.currentPage);
this.favoritesService.getFavorites(this.postsPerPage, this.currentPage);
this.userId = this.authService.getUserId();
this.postsSub = this.postsService
.getPostUpdateListener()
.subscribe((postData: { posts: Post[]; postCount: number }) => {
this.totalPosts = postData.postCount;
this.posts = postData.posts;
console.log("Posts fetched successful!");
});
this.favoritesSub = this.favoritesService
.getFavoriteUpdateListener()
.subscribe(
(favoriteData: { favorites: Favorite[]; postCount: number }) => {
this.isLoading = false;
this.favorites = favoriteData.favorites;
this.fetchFavorites();
console.log("Favorites fetched successful!");
}
);
this.userIsAuthenticated = this.authService.getIsAuth();
this.authStatusSub = this.authService
.getAuthStatusListener()
.subscribe((isAuthenticated) => {
this.userIsAuthenticated = isAuthenticated;
this.userId = this.authService.getUserId();
});
}
我的 post 列表只有在 post 数据先到达时才能正确显示。由于订阅的异步性,我无法控制哪些数据先到达。
我已经尝试过的是使用订阅的完整功能,但它从未被执行过。另一种方法是将收藏夹部分外包到一个自己的函数中,并在获取 post 之后执行它。两种方法都陷入了无尽的加载循环。
是否可以先等待post数据到达?
您有多种选择来实现您想要的行为。
选项 1:
您可以使用 RxJS 运算符 switchMap
,它会在订阅发出后立即执行,并 returns 一个新的 Observable。有关 switchMap
的更多信息,请参阅 here ;)
我以你的电话 getPostUpdateListener
和 getFavoriteUpdateListener
为例,所以它看起来像这样:
...
this.postsSub = this.postsService
.getPostUpdateListener()
.pipe(
switchMap((postData: { posts: Post[]; postCount: number }) => {
this.totalPosts = postData.postCount;
this.posts = postData.posts;
console.log("Posts fetched successful!");
return this.favoritesService.getFavoriteUpdateListener();
})
)
.subscribe((favoriteData: { favorites: Favorite[]; postCount: number }) => {
this.isLoading = false;
this.favorites = favoriteData.favorites;
this.fetchFavorites();
console.log("Favorites fetched successful!");
});
...
选项 2:
您可以使用 firstValueFrom
或 lastValueFrom
来承诺您的 Observable,然后您可以等待它的执行,例如async/await。有关详细信息,请参阅 here ;)
这看起来像下面这样:
async ngOnInit() {
...
const postData: { posts: Post[]; postCount: number } = await firstValueFrom(this.postsService.getPostUpdateListener());
this.totalPosts = postData.postCount;
this.posts = postData.posts;
console.log("Posts fetched successful!");
const favoriteData: { favorites: Favorite[]; postCount: number } = await firstValueFrom(this.favoritesService.getFavoriteUpdateListener());
this.isLoading = false;
this.favorites = favoriteData.favorites;
this.fetchFavorites();
console.log("Favorites fetched successful!");
...
}
由于 Angular 以反应方式工作了很多,我会选择选项 1 ;)
对于干净的代码,您可以使用 rxjs forkJoin
运算符来达到确切的目的。基本上,您拥有的是两个在组件初始化时订阅的可观察对象。 forkJoin
致力于加入多个可观察流,或者为了更好地理解考虑 Promise.all
将如何等待所有可能的 Promises
.
不使用 subscribe
和用户 .toPromise()
并等待的另一个选项。参考
在您的情况下,使用 combineLatest(RxJS v6) 或 combineLatestWith(RxJs v7.4) 会更好。 combineLatest 运算符将在任何源 Observables 至少发射一次后发射。
由于您的服务不相互依赖,因此在您的情况下最好使用 combineLatest。
this.postsSub = this.postsService.getPosts(this.postsPerPage, this.currentPage);
this.favoritesSub = this.favoritesService.getFavorites(this.postsPerPage, this.currentPage);
combineLatest([this.postsSub, this.favoritesSub]).pipe(
map(([postData, favoriteData]) => {
this.totalPosts = postData.postCount;
this.posts = postData.posts;
console.log("Posts fetched successful!");
this.isLoading = false;
this.favorites = favoriteData.favorites;
this.fetchFavorites();
console.log("Favorites fetched successful!");
})).subscribe();
我想显示 post 的列表,如下所示: Post List
为了显示哪个 post 受到用户的青睐,我需要来自 mongodb 数据库中两个不同 collections 的数据。 目前我的 post-list.component.ts 文件的 ngOnInit 看起来像这样:
ngOnInit() {
this.isLoading = true;
this.postsService.getPosts(this.postsPerPage, this.currentPage);
this.favoritesService.getFavorites(this.postsPerPage, this.currentPage);
this.userId = this.authService.getUserId();
this.postsSub = this.postsService
.getPostUpdateListener()
.subscribe((postData: { posts: Post[]; postCount: number }) => {
this.totalPosts = postData.postCount;
this.posts = postData.posts;
console.log("Posts fetched successful!");
});
this.favoritesSub = this.favoritesService
.getFavoriteUpdateListener()
.subscribe(
(favoriteData: { favorites: Favorite[]; postCount: number }) => {
this.isLoading = false;
this.favorites = favoriteData.favorites;
this.fetchFavorites();
console.log("Favorites fetched successful!");
}
);
this.userIsAuthenticated = this.authService.getIsAuth();
this.authStatusSub = this.authService
.getAuthStatusListener()
.subscribe((isAuthenticated) => {
this.userIsAuthenticated = isAuthenticated;
this.userId = this.authService.getUserId();
});
}
是否可以先等待post数据到达?
您有多种选择来实现您想要的行为。
选项 1:
您可以使用 RxJS 运算符 switchMap
,它会在订阅发出后立即执行,并 returns 一个新的 Observable。有关 switchMap
的更多信息,请参阅 here ;)
我以你的电话 getPostUpdateListener
和 getFavoriteUpdateListener
为例,所以它看起来像这样:
...
this.postsSub = this.postsService
.getPostUpdateListener()
.pipe(
switchMap((postData: { posts: Post[]; postCount: number }) => {
this.totalPosts = postData.postCount;
this.posts = postData.posts;
console.log("Posts fetched successful!");
return this.favoritesService.getFavoriteUpdateListener();
})
)
.subscribe((favoriteData: { favorites: Favorite[]; postCount: number }) => {
this.isLoading = false;
this.favorites = favoriteData.favorites;
this.fetchFavorites();
console.log("Favorites fetched successful!");
});
...
选项 2:
您可以使用 firstValueFrom
或 lastValueFrom
来承诺您的 Observable,然后您可以等待它的执行,例如async/await。有关详细信息,请参阅 here ;)
这看起来像下面这样:
async ngOnInit() {
...
const postData: { posts: Post[]; postCount: number } = await firstValueFrom(this.postsService.getPostUpdateListener());
this.totalPosts = postData.postCount;
this.posts = postData.posts;
console.log("Posts fetched successful!");
const favoriteData: { favorites: Favorite[]; postCount: number } = await firstValueFrom(this.favoritesService.getFavoriteUpdateListener());
this.isLoading = false;
this.favorites = favoriteData.favorites;
this.fetchFavorites();
console.log("Favorites fetched successful!");
...
}
由于 Angular 以反应方式工作了很多,我会选择选项 1 ;)
对于干净的代码,您可以使用 rxjs forkJoin
运算符来达到确切的目的。基本上,您拥有的是两个在组件初始化时订阅的可观察对象。 forkJoin
致力于加入多个可观察流,或者为了更好地理解考虑 Promise.all
将如何等待所有可能的 Promises
.
不使用 subscribe
和用户 .toPromise()
并等待的另一个选项。参考
在您的情况下,使用 combineLatest(RxJS v6) 或 combineLatestWith(RxJs v7.4) 会更好。 combineLatest 运算符将在任何源 Observables 至少发射一次后发射。 由于您的服务不相互依赖,因此在您的情况下最好使用 combineLatest。
this.postsSub = this.postsService.getPosts(this.postsPerPage, this.currentPage);
this.favoritesSub = this.favoritesService.getFavorites(this.postsPerPage, this.currentPage);
combineLatest([this.postsSub, this.favoritesSub]).pipe(
map(([postData, favoriteData]) => {
this.totalPosts = postData.postCount;
this.posts = postData.posts;
console.log("Posts fetched successful!");
this.isLoading = false;
this.favorites = favoriteData.favorites;
this.fetchFavorites();
console.log("Favorites fetched successful!");
})).subscribe();