我正在尝试创建一个服务工作者,该服务工作者将从API提供有关用户的信息。
我的想法是我不想发出无用的请求,例如两次询问同一位用户,因此我正在尝试实现一个函数,该函数检查我们是否存储了用户数据,以及是否从服务器获取了该数据。
我尝试使用for i in range(0, 10, 2, 4, 5, 6, 7, 8):
,mapTo
,share
等管道
shareReplay
users = new BehaviorSubject<Array<User>>([]);
所以当我用相同的ID同时调用getUser 2次时,它应该只请求一次。
答案 0 :(得分:0)
要为行为主体中的数组添加一个值,而又不使该数组的内容发生突变,请创建一个新数组,然后使行为主体发出该新数组。
this.users.next([..this.users.value, user]);
要使用值创建新的可观察值,只需使用
of(user);
您的api为什么返回承诺? http服务返回可观测值,因此您应该能够只返回映射到用户对象的http。
return this.api.get('users/' + id, this.userService.getAuthToken(), ).pipe(
map(functionToMapResponseToAUserObject),
tap(sideEfectFunctionToInsertNewUserIntoBehaviorSubject)
)
因此,如果您修改api以返回可观察的结果而不是应许的结果,则可能是您的服务
getUserInfo(id: string): Observable<User> {
const users = this.users.getValue();
const user = users.find((u) => u._id === id);
return user
? of(user)
: this.api.get('users/' + id, this.userService.getAuthToken(), ).pipe(
map(response => new User(response)),
tap(user => { this.users.next([...users, user]); })
)
}
如果您无法修改api,请使用from将诺言转换为可观察的
from(this.api.get('users/' + id, this.userService.getAuthToken(), ))
要停止对同一id的多个请求,您需要一个对象来跟踪正在加载的请求
loading: { [key]: Observable<User> } = {};
将共享重播添加到api调用中,这样就不会重复api调用并将其粘贴到加载缓存中
let loaded = false;
const user$ = this.api.get('users/' + id, this.userService.getAuthToken(), ).pipe(
map(response => new User(response)),
tap(user => {
this.users.next([...users, user]);
if (loading[id]) {
delete loading[id];
}
loaded = true;
}),
shareReplay()
);
if (!loaded) {
loading[id] = user$;
}
然后您可以在触发api调用之前查看是否正在加载,并从加载缓存中返回可观察到的内容。
答案 1 :(得分:0)
阿德里安的答案很接近,但不能完全回答问题的一部分:
所以当我用相同的ID同时调用getUser 2次时,它应该只请求一次。
Adrian的解决方案不会执行此操作,因为如果请求仍未解决,则该用户将不会被存储,您最终将两次请求该用户。
通常建议的方法是将缓存存储为可观察对象的映射。
伪打字稿,语法可能是错误的,我还将假设您可以将api调用变成可观察的,如果没有,请寻找Adrian关于如何处理它的答案:
users: Map<string,Observable<User>> = new Map();
getUserInfo(id: string): Observable<User> {
if(!this.users.get(id)){
const userObservable = this.api.get('users/' + id, this.userService.getAuthToken(), ) // Or turn the promise to observable with `from`
.pipe(
map(response => new User(response)),
share(),
);
this.users.set(id, userObservable)
}
return this.users.get(id)
}
答案 2 :(得分:0)
请尝试这种方法,我相信它将满足您的需求
usersSubject = new Subject<User>();
usersCache = this.usersSubject.pipe(
scan<Record<string, User>>((cache, user) => {
cache[user.id] = user;
return cache;
}, {}),
shareReplay(1) // keeping the last version of the cache
);
getUserInfo(id: string): Observable<User> {
return this.usersCache.pipe(
pluck(id),
switchMap(user => iif(() => !user,
this.api.get('users/' + id, this.userService.getAuthToken()).pipe(
tap(user => this.usersSubject.next(user)) // caching the user
),
of(user)
),
first() // auto unsubscription, because usersCache will keep pushing values
)
}
更新: 确保保持订阅为打开状态以保持缓存完整(我想这是一项服务,因此请在构造函数中进行操作):
this.usersCache.subscribe()