我有一个 Angular 应用程序,它每 2 分钟更新一次 jwt。有一个潜在的问题是,当 jwt 更新时,用户可能会执行一些操作会触发另一个 http 调用 go with old jwt。
如何确保在另一个潜在的 http 调用触发之前完成 renewjwt()?
我最初的想法是在 renewjwt() 调用之前和完成后的回调函数中切换一个全局标志。
有没有使用原生 Angular 或 rxjs 完成此任务的好方法?
我有一个 Angular 应用程序,它每 2 分钟更新一次 jwt。有一个潜在的问题是,当 jwt 更新时,用户可能会执行一些操作会触发另一个 http 调用 go with old jwt。
如何确保在另一个潜在的 http 调用触发之前完成 renewjwt()?
我最初的想法是在 renewjwt() 调用之前和完成后的回调函数中切换一个全局标志。
有没有使用原生 Angular 或 rxjs 完成此任务的好方法?
您可以使用delayWhen
运算符来延迟基于另一个可观察到的发射值。
为此,您可以创建 observable,如下所示:
function renewTokenIfNeeded$(): Observable<void> {
console.log('Renew token start');
/**
* Here you should have have :
* If current token is valid,
* ---- then return of(number),
* else
* ---- then return this.http.post([...])
*/
// for demonstration prupose i always renew the token.
return of(null).pipe(delay(
Math.floor(Math.random() * 1000) + 1
), tap(() => console.log('renew token stop')));
}
/**
* Each 100 ms i trigger new dummy http request. By exhaustMap i will ignore all future "interval" emission until my dummy http request is complete stream.
*/
const renewTokensTimer$ = interval(100).pipe(exhaustMap(() => renewTokenIfNeeded$()));
数据流将如下所示:
|...|...|...|................|...|...|...|
1 / 像这样我有流,它将每 100 毫秒发出新的数字,直到你认为你的电流是脏的。
2/ 然后它会执行 http 请求来获取新的请求。
3/ 间隔源 observable 不会发出新值,直到您耗尽 http 请求。
那么你可以像下面这样简单地使用它:
of(null) // Create dummy observable.
.pipe(delayWhen(() => renewTokenIfNeeded$())) // Delay it if token have to be renew.
.pipe(mergeMap(() => myRegularHttpRequest$)) // perform my http request, i am 100% here i have fresh JWT
.subscribe(console.log) // You can safely consume the API answer.
我通过Promise
在已发送且尚未收到回复时存储待处理的 Promise 以进行续订来做到这一点。
getToken
auth 服务的函数返回一个Promise
:
if (this.promiseRefreshToken) {
/* Path 1 */
this.log.debug('Refresh token is already pending, waiting for the answer to be received.');
return this.promiseRefreshToken;
} else {
/* Path 2 */
if /* no need to renew */
return Promise.resolve(token);
else
return getRefreshToken();
}
该refreshToken
函数实际上负责正确设置promiseRefreshToken
(服务的成员变量):
return this.promiseRefreshToken = this.api_query./* API QUERY FOR TOKEN */
.then(api_resp => {
this.promiseRefreshToken = null;
return /* the token */;
})
.catch(api_error => {
this.promiseRefreshToken = null;
return Promise.reject('Error...');
});
当回复真正到来时,不要忘记将 恢复Promise
为 null 以便后续调用使用通常的逻辑(上面的路径 2)。
这样,可以保证您永远不会有两个并发的令牌刷新请求。