Javascript RxJs-创建可观测数组的正确模式/方法是什么?
我对RxJs还很陌生,我想知道正确的模式是什么,简单地创建一个可观察的可观察数组 我想检索用户帖子的列表。帖子本身应该是可观察的,我想把它们都保存在一个可观察的数组中,这样当数组改变时,调用代码应该被通知,并更新任何订阅到帖子“列表”中的内容。这很简单,但我也希望每个帖子都是可观察的,因此如果我从中检索到一个特定的帖子[I],我也应该能够订阅这些单独的对象。 正确的方法是什么 我使用Angular 9,我有:Javascript RxJs-创建可观测数组的正确模式/方法是什么?,javascript,rxjs,observable,publish-subscribe,Javascript,Rxjs,Observable,Publish Subscribe,我对RxJs还很陌生,我想知道正确的模式是什么,简单地创建一个可观察的可观察数组 我想检索用户帖子的列表。帖子本身应该是可观察的,我想把它们都保存在一个可观察的数组中,这样当数组改变时,调用代码应该被通知,并更新任何订阅到帖子“列表”中的内容。这很简单,但我也希望每个帖子都是可观察的,因此如果我从中检索到一个特定的帖子[I],我也应该能够订阅这些单独的对象。 正确的方法是什么 我使用Angular 9,我有: public getPosts(): Observable<Array<P
public getPosts(): Observable<Array<Post>> {
return new Promise((resolve, reject) = {
let posts: Observable<Array<Post>> = new Observable<Array<Post>>();
this.get<Array<Post>>('posts').subscribe(r => {
posts = from(r);
return resolve(posts);
});
});
}
public getPosts():可观察{
返回新承诺((解决、拒绝)={
let posts:Observable=新Observable();
this.get('posts').subscribe(r=>{
职位=来自(r);
返回(员额);
});
});
}
这给了我一个可观察的
,但是我应该如何创建一个可观察的
?
这是反模式吗 这一切都很方便,如果您的服务器为您提供了post中更改内容的差异数据,那么就继续创建
可观察的
然而,在你的帖子中,有很多问题。您不能将可观察对象
与承诺
混合使用。方法getPosts
将只返回从API获得的第一篇文章
这是你想要的解决方案,但我不确定,这是你真正想要的
public getPosts(): Observable<Array<Observable<Post>>> {
return this.get('posts').pipe(
switchMap(posts => combineLatest(
posts.map(post => this.get('post', post.id))
)),
);
}
public getPosts():可观察{
返回此.get('posts').pipe(
开关映射(posts=>CombineTest(
posts.map(post=>this.get('post',post.id))
)),
);
}
不清楚您在这里想要实现什么,但您可能想要更像这样的东西:
@Injectable({providedIn:'root'})
export class PostService {
// private replay subject will cache one value
private postSource = new ReplaySubject<Post[]>(1)
// public list of posts observable
posts$ = this.postSource.asObservable();
// function to select item by id out of list
post$ = (id) => this.posts$.pipe(map(posts => posts.find(p => p.id === id)))
getPosts() {
// function to get remote posts
return this.get<Post[]>('posts');
}
loadPosts() {
// function to load posts and set the subject value
this.getPosts().subscribe(posts => this.postSource.next(posts));
}
}
@Injectable({providedIn:'root'})
出口邮递服务{
//私有重播主题将缓存一个值
private postSource=新的ReplaySubject(1)
//可观察职位的公开名单
posts$=this.postSource.asObservable();
//从列表中按id选择项目的函数
post$=(id)=>this.posts$.pipe(映射(posts=>posts.find(p=>p.id==id)))
getPosts(){
//获取远程帖子的函数
返回这个。get('posts');
}
装货柱(){
//函数加载帖子并设置主题值
this.getPosts().subscribe(posts=>this.postSource.next(posts));
}
}
您必须定义get
函数,并在每次更新列表时调用loadPosts
。给定信息:
!!如果这些陈述有任何错误,请告诉我,我会更新答案
get函数,该函数返回一个可观测值,其中一个数组中填充了post
“获取可观测”总是在立柱发生变化时发出
可观测值(数组>)内的值不可观测,且不会随时间变化
()=>getPosts$
提示:首先尝试从return语句中读取函数。然后检查映射/筛选或其他操作中发生的情况。希望我没有把你弄糊涂。如果您有任何问题,请随时提问。确实不清楚您打算如何解决这一问题。。。。http客户端在该时间点对POST
发出单个请求。如果您不需要再次请求,它不会收到更改通知。我不确定您为什么要这样做。一般来说,这是一种反模式。就可读性和可维护性而言,创建嵌套的可观察对象从来都不是一个好主意。rxjs中有大量的内置操作符,只是为了使嵌套的观测值变平,以防止它成为问题。感谢@Xinan,这很有意义。。。我只是想确定如何在每次需要时检索单个可观察对象,而无需在任何客户机请求时重新创建新的可观察对象(它应该返回现有的可观察对象,如果存在的话),因此我认为我只需要在创建现有可观察对象时缓存或存储它们,并返回它们,或者创建一个新的…感谢您提供的详细信息!有道理:)。。。我知道如何检索一个可观察到的,但是我想知道是否有一个合适的方法可以让帖子本身在数组中被观察到,但是也许这不是思考它的方式。。。相反,应该是一个可观察的帖子数组(如您所示),然后使用单独的getPostById()函数将常规帖子转换为可观察的帖子。。。我只是不确定多个单独调用getPostById()的客户机是否会返回完全不同的观察值。。。那么更改一个不会将事件发送给其他人?我同意这里关于可观察状态管理的一般想法,但我认为如果您更多地遵循带有单个操作分派主题的存储类型模型,它将发出状态修改函数和有效负载(如果需要的话),那么它将更干净、更通用/可重用。谢谢。我需要更多地了解这种模式。。。我有一个存储区,存储区基本上应该只提供两件事:1)当前帖子数组(可观察的),和2)检索特定帖子的接口,它应该从这个数组中提取帖子,并作为可观察的返回。。但是可观察的返回应该是任何其他调用都会得到的相同的可观察结果(即,它不应该使一个新的可观察结果成为我所称的getPostById(id))。。。所以试着让我的头绕着它转。也许在你的解决方案中,但我需要玩一下。谢谢!我认为,您的解决方案是有道理的,但据我所知,它将在请求所有帖子时请求每个帖子(使用这个.get('post,post.id'),将它们全部转化为可观察的内容)。。。这难道不是可以避免的吗,因为我已经有了完整的帖子了?难道没有办法把它们都变成可以观察到的智慧吗
this.get<Array<Post>>('posts')
// This function returns you an observable with the post related to your id.
// If the id is not found the observable will not emit
// If the id is found the observable will only emit if the interface values have been changed
function getPostById$(id: string): Observable<Post> {
// Returns you either the post or undefined if not found
const findId = (id: string) => (posts: Array<Post>): Post | undefined =>
posts.find(post => post.id === id);
// Allows you only to emit, if id has been found
const existingPost = (post: Post | undefined): boolean => post != null;
// Allows you only to emit if your id has been changed
const postComparator = (prevPost: Post, currPost: Post): boolean =>
prevPost.value === currPost.value && prevPost.name === currPost.name;
return this.get('posts').pipe(
map(findId(id)),
filter(existingPost),
distinctUntilChanged(postComparator)
);
}
function getPosts$(): Observable<Array<Post>> {
return this.get('posts');
}
// This function allows to manage your own state
// 1. posts$: overwrites all posts
// 2. clear$: empties your posts$ observable
// 3. add$: adds one observable to the end of your posts
function statePosts$(posts$: Observable<Array<Posts>>, clear$: Observable<void>, add$: Observable<Post>): Observable<Array<Post>> {
const updatePosts = (newPosts: Array<Posts>) => (oldPosts: Array<Posts>) => newPosts;
const clearPosts = () => (oldPosts: Array<Posts>) => [];
const addPost = (post: Post) => (oldPosts: Array<Posts>) => [...oldPosts, post];
return merge(
// You can add as much update functions as you need/want (eg: deleteId, addPostAtStart, sortPosts, ...)
posts$.pipe(map(updatePosts)),
clear$.pipe(map(clearPosts)),
add$.pipe(map(addPost))
).pipe(
// The fn in you scan is the (oldPosts: Array<Posts>) function from one of your three update functions (updatePosts, clearPosts and addPosts).
// Whenever one of those three observables emits it first calls the left side of the function inside the map (post: Post) and returns a new function
// When this function reaches the scan it gets the oldPosts and is able to update it
scan((oldPosts, fn) => fn(oldPosts), [])
)
}
// Usage
private posts$: Observable<Array<Post>> = this.get('posts');
private clear$: Subject<void> = new Subject();
private add$: Subject<Post> = new Subject();
public statePosts$ = getStatePosts(posts$, clear$, add$);