向后台发起API请求,但后台API不稳定,有时候超一分钟才能返回结果,有报5XX服务端的错误,那我们怎么处理这种情况呢。
直接看如下代码,使用retry + timeoutWith + catchError 组合
import {of, forkJoin, Observable, throwError} from 'rxjs';
import {catchError, map, mergeMap, retry, timeoutWith} from 'rxjs/operators';
return this.common.get(`https://xxx.com/api/xxx`)
.pipe(
retry(3),
timeoutWith(15000, throwError(new Error('Http Timeout exceeds'))),
map((res: any) => {
return res.data;
}),
catchError(err => {
console.log('Call API failed::', err);
return of(initData);
})
);
如果向后台发起一次API请求,然后重试3次都失败,一共4次请求都失败,这4次请求所花费的时间如果小于15秒,那它是不会抛出超时的错误,而是抛出API的错误,否则的话,会抛出超时错误 。 也就是说timeoutWith里面设置的时间是与前面4次请求所花时间的总和来比较的。
拿我现在的项目来说, API 通常在第6秒抛出500错误,API的错误内容是: internal error.
下面代码的请求过程是这样的:
1. 发起第一次请求,等待了6秒,API 抛错 internal error.
2. retry第一次, 请求也花了6秒, 共花了12秒,API 继续抛错 internal error.
3. retry第二次,在请求等待第3秒时,(6+6+3 = 15),就不再继续等待了,API 还没来得及返回任何信息
4. 由于有timeoutWith(15秒),程序直接抛出了'Http Timeout exceeds'错误
5. catchError捕获了超时的错误,而不是API的internal error
注意:在步骤3这里,http 请求是未结束的,如果你在拦截器里写了http请求结束后的逻辑,它是不会被执行的。
这里在pipe里使用了三个函数:
下面来一段更清晰的。推荐用这个编辑器(https://stackblitz.com/)
// RxJS v6+
import { of,throwError, Observable } from 'rxjs';
import { retry, concatMap, mergeMap, timeout, timeoutWith, catchError, delay } from 'rxjs/operators';
// simulate request
function makeRequest(timeToDelay) {
return of('Request Complete!').pipe(
delay(timeToDelay),
mergeMap(()=>{
console.log('ttt');
return throwError('1 timeout!')
})
);
}
makeRequest(4000).pipe(
retry(3),
timeoutWith(15000, throwError('Http Timeout exceeds')),
catchError(error => {
console.log(error);
return of(`Request timed out after: 4000`)
})
).subscribe(val => console.log(val));
结果为:
第一次请求,花了4秒, 打印ttt
第二次请求(retry 第一次),花了4秒,打印ttt
第三次请求(retry 第二次),花了4秒,打印ttt
第四次请求(retry 第三次),花了3秒 (4+4+4+3 = 15), 不等makeRequest返回就强制让它超时,并抛出'Http Timeout exceeds'超时错误
https://www.learnrxjs.io/learn-rxjs/operators/utility/timeoutwith