Return 在满足某些条件后接收值的 Rxjs Observable
Return Rxjs Observable that receives a value after some condition is fulfilled
我正在实现一个接口,该接口具有 returns Observable 的功能。
我还需要向 Observable 传递一些值,但接收该值可能需要一些时间。
我怎样才能 return Observable 并让它等待所需的值?
更具体地说,我正在实现一个 HttpInterceptor,我想为请求设置一个令牌 header。
令牌值可能不可用,因此需要稍等(异步)并重试,直到收到该值。
然后在请求中设置tokenheader然后继续。
如何实现这样的机制?
@Injectable()
export class HttpXsrfInterceptor implements HttpInterceptor {
constructor(private tokenService: HttpXsrfTokenExtractor) { }
getToken(callback) {
let token = this.tokenService.getToken();
if (!token) {
// a valid token wasn't received. wait a little and try again
setTimeout(() => {
this.getToken(callback); //recursive call
}, 1000);
} else {
// found valid token
callback(token);
}
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// this part should set req when a token is received, but it is asynchronous
this.getToken((token) => {
req = req.clone({headers: req.headers.set('X-XSRF-TOKEN', token)});
});
// this returns Observable. I must return Observable, but req is not ready at this point
return next.handle(req);
}
}
最简单的方法是使用 RxJs 运算符。使用 switchMap
应该是一个很好的解决方案。本质上,在这种情况下,switchMap 允许您将依赖的 observable 链接在一起,并且仅 return 内部 observable。它应该看起来像这样:
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return getToken.pipe(
switchMap(token => {
req = req.clone({headers: req.headers.set('X-XSRF-TOKEN', token)});
return next.handle(req);
}
);
}
请注意,您还需要将 getToken 调整为 return 可观察值才能使其正常工作。
查看代码,似乎我们有来自 getToken
的回调和来自 intercept
的观察。如果可能,最好始终使用 observable。
我们可以将 getToken(callback)
转换为使用 observable。 RxJS 有 retryWhen
运算符,我们可以用它来处理重试。
getToken() {
const tokenFromService = of(this.tokenService.getToken()); // convert to observable
return tokenFromService
.pipe(
map(token => {
if (!token) {
throw new Error('token is not specified'); // it will be caught by retryWhen
}
return token;
}),
retryWhen(error => {
return error
.pipe(
tap(() => console.log('error happened, retry request token')),
delay(1000)
)
})
)
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return this.getToken()
.pipe(
switchMap(token => {
const modifiedReq = req.clone({headers: req.headers.set('X-XSRF-TOKEN', token)});
return next.handle(modifiedReq);
})
)
}
希望对您有所帮助
参考:
我正在实现一个接口,该接口具有 returns Observable 的功能。 我还需要向 Observable 传递一些值,但接收该值可能需要一些时间。
我怎样才能 return Observable 并让它等待所需的值?
更具体地说,我正在实现一个 HttpInterceptor,我想为请求设置一个令牌 header。 令牌值可能不可用,因此需要稍等(异步)并重试,直到收到该值。 然后在请求中设置tokenheader然后继续。
如何实现这样的机制?
@Injectable()
export class HttpXsrfInterceptor implements HttpInterceptor {
constructor(private tokenService: HttpXsrfTokenExtractor) { }
getToken(callback) {
let token = this.tokenService.getToken();
if (!token) {
// a valid token wasn't received. wait a little and try again
setTimeout(() => {
this.getToken(callback); //recursive call
}, 1000);
} else {
// found valid token
callback(token);
}
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// this part should set req when a token is received, but it is asynchronous
this.getToken((token) => {
req = req.clone({headers: req.headers.set('X-XSRF-TOKEN', token)});
});
// this returns Observable. I must return Observable, but req is not ready at this point
return next.handle(req);
}
}
最简单的方法是使用 RxJs 运算符。使用 switchMap
应该是一个很好的解决方案。本质上,在这种情况下,switchMap 允许您将依赖的 observable 链接在一起,并且仅 return 内部 observable。它应该看起来像这样:
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return getToken.pipe(
switchMap(token => {
req = req.clone({headers: req.headers.set('X-XSRF-TOKEN', token)});
return next.handle(req);
}
);
}
请注意,您还需要将 getToken 调整为 return 可观察值才能使其正常工作。
查看代码,似乎我们有来自 getToken
的回调和来自 intercept
的观察。如果可能,最好始终使用 observable。
我们可以将 getToken(callback)
转换为使用 observable。 RxJS 有 retryWhen
运算符,我们可以用它来处理重试。
getToken() {
const tokenFromService = of(this.tokenService.getToken()); // convert to observable
return tokenFromService
.pipe(
map(token => {
if (!token) {
throw new Error('token is not specified'); // it will be caught by retryWhen
}
return token;
}),
retryWhen(error => {
return error
.pipe(
tap(() => console.log('error happened, retry request token')),
delay(1000)
)
})
)
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return this.getToken()
.pipe(
switchMap(token => {
const modifiedReq = req.clone({headers: req.headers.set('X-XSRF-TOKEN', token)});
return next.handle(modifiedReq);
})
)
}
希望对您有所帮助
参考: