Error handling with RxJS
Summary
Imperative error handling has many drawbacks that make it incompatible with FP.
Value containers, likeTry, provide a fluent, expressive mechanism for transforming values immutably.
The Try wrapper is a functional data type used to consolidate and abstract exception handling so that you can sequentially map functions to values.
RxJS implements many useful and powerful operators that allow you to catch and retry failed operations in a way that doesn’t break the flow of the stream and the declarative nature of an RxJS stream declaration.
RxJS provides operators such as catch(),retry(),retryWhen(), andfinally() that you can combine to create sophisticated error-handling schemes.
1.下面这种错误处理方式,有很多缺点,这和函数式编程不兼容
ajax('/rest/api/data', data => {
for (let item of data) {
ajax(`/rest/api/data/${item.id}/info`, dataInfo => {
ajax(`/rest/api/data/images/${dataInfo.img}`, showImage, error => { //#A
console.log(`Error image: ${error.message}`);
});
},
error => { //#B
console.log(`Error each data item: ${error.message}`);
});
}
},
error => { //#C
console.log(`Error fetching data: ${error.message}`);
});
2.容器,比如Try,提供了一个流畅的机制,来改变值的不变???
3.Try是函数式的数据类型,用来合并抽象异常处理,以便将函数映射到值
try {
let record = findRecordById('123');
... potentially many lines of code in between
}
catch (e) {
console.log('ERROR: Record not found!');
// Handle error here
}
4.RxJS有很多有用的operators,来catch和retry
const computeHalf = x => Math.floor(x / 2);
Rx.Observable.of(2,4,5,8,10)
.map(num => {
if(num % 2 !== 0) {
throw new Error(`Unexpected odd number: ${num}`); //#A
}
return num;
})
.map(computeHalf)
.subscribe(
function next(val) {
console.log(val);
},
function error(err) {
console.log(`Caught: ${err}`); //#B
},
function complete() {
console.log('All done!');
}
);
//catch
Rx.Observable.of(2,4,5,8,10)
.map(num => {
if(num % 2 !== 0) {
throw new Error(`Unexpected odd number: ${num}`);
}
return num;
})
.catch(err => Rx.Observable.of(6)) //#A
.map(n => n / 2)
.subscribe(
function next(val) {
console.log(val);
},
function error(err) {
console.log(`Received error: ${err}`); //#B
},
function complete() {
console.log('All done!');
}
);
//Result:
1
2
3
All done!
//retryWhen
const maxRetries = 3;
Rx.Observable.of(2, 4, 5, 8, 10)
.map(num => {
if (num % 2 !== 0) {
throw new Error(`Unexpected odd number: ${num}`);
}
return num;
})
.retryWhen(errors$ =>
Rx.Observable.range(0, maxRetries) //#A
.zip(errors$, val => val) //#B
.mergeMap(i => //#C
Rx.Observable.timer(i * 1000)
.do(() => console.log(`Retrying after ${i} second(s)...`)))
)
.subscribe(console.log);
//RESULT:
2
4
Retrying after 0 second(s)...
2
4
Retrying after 1 second(s)...
2
4
Retrying after 2 second(s)...
2
4
5.catch(),retry(),retryWhen(),andfinally(),可以用来合并创造复杂的错误处理
const maxRetries = 3;
Rx.Observable.of(2,4,5,8,10)
.map(num => {
if(num % 2 !== 0) {
throw new Error(`Unexpected odd number: ${num}`);
}
return num;
})
.retryWhen(errors$ =>
Rx.Observable.range(0, maxRetries + 1)
.zip(errors$, (i, err) => ({'i': i, 'err': err})) //#A
.mergeMap( ({i, err}) => { //#B
if(i === maxRetries) {
return Rx.Observable.throw(err); //#C
}
return Rx.Observable.timer(i * 1000)
.do(() =>
console.log(`Retrying after ${i} second(s)...`));
})
)
.subscribe(
console.log,
error => console.log(error.message)
);
//Result:
2
4
Retrying after 0 second(s)...
2
4
Retrying after 1 second(s)...
2
4
Retrying after 2 second(s)...
2
4
Unexpected odd number: 5