RxJS 中的简单错误处理
创建复杂的可观察管道固然很好,但如何有效地处理其中的错误呢?让我们在这里回顾一下抓住,最后,重试和重试时间操作符。
观察者的错误回调
最基本的是,观察者使用错误回调来接收可观察流中任何未处理的错误。例如,这里我们的可观察对象失败,错误消息打印到控制台:
const obs$ = Rx.Observable
.interval(500)
.map(value => {
if (value > 3) {
throw new Error('too high!');
} else {
return value;
}
});
obs$.subscribe(value => {
console.log(value);
},
err => {
console.error('Oops:', err.message);
},
() => {
console.log(`We're done here!`);
});
以下是控制台中打印的内容:
0
1
2
3
Oops: too high!
Catch 操作符
将未处理的错误传播给观察者应该是最后的手段,因为我们可以使用抓住运算符来处理流中发生的错误。如果没有其他 catch 运算符,则Catch应返回另一个可观察对象或再次抛出,以便由下一个 catch 运算符或观察者的错误处理程序处理。
例如,在这里我们返回一个可观察的值3:
const obs$ = Rx.Observable
.interval(500)
.map(value => {
if (value > 3) {
throw new Error('too high!');
} else {
return value;
}
})
.catch(error => {
return Rx.Observable.of(3);
});
obs$.subscribe(value => {
console.log(value);
},
err => {
console.error('Oops:', err.message);
},
() => {
console.log(`We're done here!`);
});
以下是我们在控制台上看到的内容。请注意,在使用catch返回的可观察对象完成后,我们的主要可观察对象流是如何完成的:
0
1
2
3
3
We're done here!
一个流可以有多个抓住根据需要对操作员进行检查,并且通常最好在流中可能失败的步骤附近进行捕获。
如果您希望流完成而不返回任何值,您可以返回一个空的可观察对象:
.catch(error => {
return Rx.Observable.empty();
})
或者,如果你想要观察者继续挂起并阻止完成,你可以返回一个绝不可观察的:
.catch(error => {
return Rx.Observable.never();
})
返回源可观察对象
抓住还可以接受第二个参数,即源可观察对象。如果你返回此源,可观察对象将有效地重新启动并重试:
const obs$ = Rx.Observable
.interval(500)
.map(value => {
if (value > 3) {
throw new Error('too high!');
} else {
return value;
}
})
.catch((error, source$) => {
return source$;
})
这是我们得到的:
0
1
2
3
0
1
2
3
0
1
2
3
...
您需要小心谨慎,仅在错误间歇发生时才返回源可观察对象。否则,如果流继续失败,您将创建一个无限循环。有关更灵活的重试机制,请参阅下文有关retry和retryWhen 的内容
最后
您可以使用最后操作符运行操作,无论可观察对象是否成功完成或出现错误。这对于在出现未处理的错误时进行清理非常有用。提供给最后将会一直运行。下面是一个简单的示例:
const obs$ = Rx.Observable
.interval(500)
.map(value => {
if (value > 3) {
throw new Error('too high!');
} else {
return value;
}
})
.finally(() => {
console.log('Goodbye!');
});
obs$.subscribe(value => {
console.log(value);
},
err => {
console.error('Oops:', err.message);
},
() => {
console.log(`We're done here!`);
});
输出以下内容:
0
1
2
3
Oops: too high!
Goodbye!
重试
重试操作符
您可以使用重试操作符从头开始重试可观察流。如果没有参数,它将无限期重试;如果传入参数,它将重试指定的次数。
在下面的例子中,我们重试 2 次,因此我们的可观察序列总共运行 3 次,最后传播到观察者的错误处理程序:
const obs$ = Rx.Observable
.interval(500)
.map(value => {
if (value > 3) {
throw new Error('too high!');
} else {
return value;
}
})
.retry(2)
obs$.subscribe(value => {
console.log(value);
},
err => {
console.error('Oops:', err.message);
},
() => {
console.log(`We're done here!`);
});
输出结果如下:
0
1
2
3
0
1
2
3
0
1
2
3
Oops: too high!
您还可以添加抓住就在重试重试失败后捕获错误:
.retry(1)
.catch(error => {
return Rx.Observable.of(777);
});
0
1
2
3
0
1
2
3
777
We're done here!
retryWhen 操作符
使用重试操作符很好,但我们经常想重试从后端获取数据,如果失败了,我们可能想给它一点时间,然后再重试,不必要地增加服务器的负担。重试时间操作符允许我们这样做。retryWhen接受可观察的错误,您可以返回该序列,并通过额外的延迟来分散重试。
这里我们在重试之间等待 500 毫秒:
const obs$ = Rx.Observable
.interval(500)
.map(value => {
if (value > 3) {
throw new Error('too high!');
} else {
return value;
}
})
.retryWhen(error$ => {
return error$.delay(500);
});
如果错误持续发生,上述代码将永远重试。要重试一定次数,您可以使用扫描操作员跟踪已进行了多少次重试,如果重试次数超过一定次数,则将错误抛出链中。
在第四次重试时,我们将放弃并让错误传播给观察者:
const obs$ = Rx.Observable
.interval(500)
.map(value => {
if (value > 3) {
throw new Error('too high!');
} else {
return value;
}
})
.retryWhen(error$ => {
return error$.scan((count, currentErr) => {
if (count > 3) {
throw currentErr;
} else {
return count += 1;
}
}, 0);
});