RxJS mergeMap 运算符内部的错误处理
Error handling inside RxJS mergeMap operator
当我使用 Angular HttpClient 发出 GET 请求时,我得到一个 observable 返回并在 RxJS 运算符 mergeMap 中处理它。
现在碰巧一次又一次抛出404,我想抓住。最后,浏览器控制台中不应出现任何错误消息,管道应使用流的下一个值进行处理。
有这种可能吗?我没有用 catchError() 管理它。
这是我的代码的简化版本:
...
this.service1.getSomeStuff().pipe(
mergeMap((someStuff) => {
return from(stuff);
}),
mergeMap((stuff) => {
return this.service2.getMoreStuff(stuff.id); // Here I need some error handling, if 404 occurs
}),
mergeMap((things) => {
return from(things).pipe(
mergeMap((thing) => {
if (allLocations.some(x => x.id === metaData.id)) {
return this.service2.getMore(thing.id, thing.type, thing.img_ref);
}
}),
map((thing) => {
...
更新:添加了 catchError() 方法
我这样试过,但是没有检测到错误,下一个mergeMap也不起作用(IDE无法识别像thing.id这样的参数, thing.type, thing.img_ref):
...
this.service1.getSomeStuff().pipe(
mergeMap((someStuff) => {
return from(stuff);
}),
mergeMap((stuff) => {
return this.service2.getMoreStuff(stuff.id).pipe(
catchError(val => of(`Error`))
);
}),
mergeMap((things) => {
return from(things).pipe(
mergeMap((thing) => {
if (allLocations.some(x => x.id === metaData.id)) {
return this.service2.getMore(thing.id, thing.type, thing.img_ref);
}
}),
map((thing) => {
...
您需要使用 retry
或 retryWhen
(名称非常不言自明)——这些操作符将重试失败的订阅(重新订阅源可观察对象,一旦发出错误.
要在每次重试时提高 id
— 您可以将其锁定在一个范围内,如下所示:
const { throwError, of, timer } = rxjs;
const { tap, retry, switchMap } = rxjs.operators;
console.log('starting...');
getDetails(0)
.subscribe(console.log);
function getDetails(id){
// retries will restart here
return of('').pipe(
switchMap(() => mockHttpGet(id).pipe(
// upon error occurence -- raise the id
tap({ error(err){
id++;
console.log(err);
}})
)),
retry(5) // just limiting the number of retries
// you could go limitless with `retry()`
)
}
function mockHttpGet(id){
return timer(500).pipe(
switchMap(()=>
id >= 3
? of('success: ' + id)
: throwError('failed for ' + id)
)
);
}
<script src="https://unpkg.com/rxjs@6.4.0/bundles/rxjs.umd.min.js"></script>
请注意,有条件 retry
仅在出现 404
错误时重试会更明智。这可以通过 retryWhen
实现,例如
// pseudocode
retryWhen(errors$ => errors$.pipe(filter(err => err.status === '404')))
选中此 article on error handling in rxjs 以通过 retry
和 retryWhen
变得更加富裕。
希望对您有所帮助
更新:还有其他方法可以实现:
const { throwError, of, timer, EMPTY } = rxjs;
const { switchMap, concatMap, map, catchError, take } = rxjs.operators;
console.log('starting...');
getDetails(0)
.subscribe(console.log);
function getDetails(id){
// make an infinite stream of retries
return timer(0, 0).pipe(
map(x => x + id),
concatMap(newId => mockHttpGet(newId).pipe(
// upon error occurence -- suppress it
catchError(err => {
console.log(err);
// TODO: ensure its 404
// we return EMPTY, to continue
// with the next timer tick
return EMPTY;
})
)),
// we'll be fine with first passed success
take(1)
)
}
function mockHttpGet(id){
return timer(500).pipe(
switchMap(()=>
id >= 3
? of('success: ' + id)
: throwError('failed for ' + id)
)
);
}
<script src="https://unpkg.com/rxjs@6.4.0/bundles/rxjs.umd.min.js"></script>
当我使用 Angular HttpClient 发出 GET 请求时,我得到一个 observable 返回并在 RxJS 运算符 mergeMap 中处理它。
现在碰巧一次又一次抛出404,我想抓住。最后,浏览器控制台中不应出现任何错误消息,管道应使用流的下一个值进行处理。
有这种可能吗?我没有用 catchError() 管理它。
这是我的代码的简化版本:
...
this.service1.getSomeStuff().pipe(
mergeMap((someStuff) => {
return from(stuff);
}),
mergeMap((stuff) => {
return this.service2.getMoreStuff(stuff.id); // Here I need some error handling, if 404 occurs
}),
mergeMap((things) => {
return from(things).pipe(
mergeMap((thing) => {
if (allLocations.some(x => x.id === metaData.id)) {
return this.service2.getMore(thing.id, thing.type, thing.img_ref);
}
}),
map((thing) => {
...
更新:添加了 catchError() 方法
我这样试过,但是没有检测到错误,下一个mergeMap也不起作用(IDE无法识别像thing.id这样的参数, thing.type, thing.img_ref):
...
this.service1.getSomeStuff().pipe(
mergeMap((someStuff) => {
return from(stuff);
}),
mergeMap((stuff) => {
return this.service2.getMoreStuff(stuff.id).pipe(
catchError(val => of(`Error`))
);
}),
mergeMap((things) => {
return from(things).pipe(
mergeMap((thing) => {
if (allLocations.some(x => x.id === metaData.id)) {
return this.service2.getMore(thing.id, thing.type, thing.img_ref);
}
}),
map((thing) => {
...
您需要使用 retry
或 retryWhen
(名称非常不言自明)——这些操作符将重试失败的订阅(重新订阅源可观察对象,一旦发出错误.
要在每次重试时提高 id
— 您可以将其锁定在一个范围内,如下所示:
const { throwError, of, timer } = rxjs;
const { tap, retry, switchMap } = rxjs.operators;
console.log('starting...');
getDetails(0)
.subscribe(console.log);
function getDetails(id){
// retries will restart here
return of('').pipe(
switchMap(() => mockHttpGet(id).pipe(
// upon error occurence -- raise the id
tap({ error(err){
id++;
console.log(err);
}})
)),
retry(5) // just limiting the number of retries
// you could go limitless with `retry()`
)
}
function mockHttpGet(id){
return timer(500).pipe(
switchMap(()=>
id >= 3
? of('success: ' + id)
: throwError('failed for ' + id)
)
);
}
<script src="https://unpkg.com/rxjs@6.4.0/bundles/rxjs.umd.min.js"></script>
请注意,有条件 retry
仅在出现 404
错误时重试会更明智。这可以通过 retryWhen
实现,例如
// pseudocode
retryWhen(errors$ => errors$.pipe(filter(err => err.status === '404')))
选中此 article on error handling in rxjs 以通过 retry
和 retryWhen
变得更加富裕。
希望对您有所帮助
更新:还有其他方法可以实现:
const { throwError, of, timer, EMPTY } = rxjs;
const { switchMap, concatMap, map, catchError, take } = rxjs.operators;
console.log('starting...');
getDetails(0)
.subscribe(console.log);
function getDetails(id){
// make an infinite stream of retries
return timer(0, 0).pipe(
map(x => x + id),
concatMap(newId => mockHttpGet(newId).pipe(
// upon error occurence -- suppress it
catchError(err => {
console.log(err);
// TODO: ensure its 404
// we return EMPTY, to continue
// with the next timer tick
return EMPTY;
})
)),
// we'll be fine with first passed success
take(1)
)
}
function mockHttpGet(id){
return timer(500).pipe(
switchMap(()=>
id >= 3
? of('success: ' + id)
: throwError('failed for ' + id)
)
);
}
<script src="https://unpkg.com/rxjs@6.4.0/bundles/rxjs.umd.min.js"></script>