我正在与使用一次性身份验证令牌的API服务器交谈,每次请求都会重新发布(即生成新令牌)。如果使用相同的令牌同时发出两个API请求,则由于令牌过期而导致失败,并且可能会不必要地触发错误处理程序。
我想要做的是构建一个等待机制,因此一次只发出一个HTTP请求,后续请求排队,直到完成前一个请求。
我想以通用的方式构建它,所以我可以有一个服务层来发出请求,所以它对上层是透明的,但我很难找到一个巧妙的方法来做到这一点。有什么建议吗?
-- edit --
我应该提到所有后续的API调用URL /请求主体都直接依赖于先前API调用的响应,例如: GET /books?apiKey=ABC
返回:
{
'apiKey': 'XYZ',
'names': [...]
}
排队的下一个请求必须等待此响应并附加新的apiKey:GET /authors?apiKey=XYZ
在上面的示例中,执行GET /authors?apiKey=ABC
会
导致错误。
如果两个HTTP请求之间存在强烈的依赖关系(例如请求书,然后根据其id请求该特定书籍的作者),则可以使用flatMap,嵌套订阅等来序列化它们。我需要类似的功能但是以通用的方式,我可以将请求添加到在运行时序列化的队列。
-- edit two --
我有两个组件,如下所示,假设两者都在启动时调用API调用。
class ComponentA {
ngOnInit(api:<BookApi extends CommonApi>) {
this.api.list().subscribe(x => {...});
}
}
class ComponentB {
ngOnInit(api:<CityLookupApi extends CommonApi>) {
this.api.list().subscribe(x => {...});
}
}
所有API服务都扩展了一个通用API来隐藏其背后的apiKey处理,因此组件可以使用更高级别的调用来帮助理解。问题是,如果两个组件几乎同时初始化,则两个调用都可以使用相同的api密钥,一个将失败。但是,我无法使用例如前面的方法创建这些Observable批次。 forkJoin创建并在运行时异步订阅,因为它们是在单独的组件中创建的。
答案 0 :(得分:2)
Yanis-git解决了排队问题。如果要在发生其他请求时阻止所有请求,请使用exhaustMap
。
答案 1 :(得分:1)
您可以使用Observable.concat(...myArrayOfAjaxRequestObservable)
逐个使用每个observable。
只有Observable.contact必须自己订阅你的ajax请求。是100%确保请求不会被代码的另一部分触发的唯一方法。
此处提供更多信息:https://www.learnrxjs.io/operators/combination/concat.html
--- --- UPDATE
示例:https://stackblitz.com/edit/angular-66ic6c?file=app%2Fapp.component.ts
答案 2 :(得分:1)
我会考虑将mergeMap
与其concurrency
参数一起使用。
这是一段模拟我想要的代码片段
import {Observable} from 'rxjs';
import {Subject} from 'rxjs';
const requestsRemoteService = new Subject<string>();
const requestStream = requestsRemoteService.asObservable();
// this function simulates the execution of the request
const requestExecution = (input: string) => {
return Observable.of(input + ' executed').delay(2000);
}
// this is what the service should to queue the requests
requestStream
// the last parameter, set to 1, is the level of concurrency
.mergeMap(requestInput => requestExecution(requestInput), 1)
.subscribe(
data => console.log(data),
err => console.error(err),
() => console.log('DONE')
)
// these are the various requests coming to the service
setTimeout(() => requestsRemoteService.next('First request'), 1);
setTimeout(() => requestsRemoteService.next('Second request'), 2);
setTimeout(() => requestsRemoteService.next('Third request'), 3);