preface
A requirement in recent work is that if this request times out, it should be retried with a configurable number of retries.
The first library we use to send requests is Axios, which processes requests in Epic in redux-Observable.
So if you want to implement the retry mechanism, there are two ways:
- In the
Axios
Add retry code to the encapsulated function - in
epic
In the useRxJS
Operator to retry.
Axios retry is a bit of a hassle, and it doesn’t feel good to add the retry code to the function already packaged. And it’s not very convenient to maintain. So use the RxJS operator to retry. The code in this article will not apply to the project code, but to write a new Demo to make it easier to understand.
RxJS error retry operator
In RxJS, two operators retry and retryWhen are provided.
Note that both operators will retry the entire sequence when retrying.
And Retry and retryWhen only capture errors, but are somewhat ineffective with promises, as explained in the solution article.
retry
The retry operator is used to specify the number of retries, such as n when an error is encountered. The following is the Demo:
const source = Rx.Observable.interval(1000)
const example = source.map(val= > {
if (val === 2) {
throw Error('error');
}
return val;
}).retry(1)
example.subscribe({
next: val= > console.log(val),
error: val= > console.log(val.message)
});
Copy the code
The online operation
The code above will issue a sequence of numbers every one second. When you subscribe, a 0 is issued one second later, a 1 is issued the second second, and so on.
And then each time the sequence of numbers arrives in the map operator, where we can see that when the sequence of numbers equals 2, an error is thrown. If it is not equal to 2, it returns unchanged and finally reaches the next function in SUBSCRIBE.
The running results are shown as follows:
Zeros and ones are emitted first, no problem, and an error is thrown when val is 2. Captured by Retry, the entire RxJS sequence is retraced. 0 and 1 are issued again, and 2 is used up again, so an error is reported, but the retry count is exhausted, and the retry is skipped. It is then caught by the error function in SUBSCRIBE. Print error.
retryWhen
The retry operator above can only be used to set the number of retries, which we sometimes want to do: print a log while retrying, or something else. Retry is not appropriate at this point. So we need retryWhen to operate.
The code is as follows:
const source = Rx.Observable.interval(1000)
const example = source.map(val= > {
if (val === 2) {
throw Error('error')}return val;
}).retryWhen(err= > {
return err
.do((a)= > console.log('Trying again'))
.delay(2000)
})
example.subscribe({
next: val= > console.log(val),
error: val= > console.log(val.message)
});
Copy the code
The online operation
The running results are shown as follows:
The sending logic is the same as above, but the processing is different.
We use the retryWhen operator to control the retry logic, print the string on the console using the DO operator, and then delay the retry by two seconds.
However, there are always retries. Where the number of retries is not set, the solution is described in the next section.
retry + retryWhen
At this point, we see that retry sets the number of retries and retryWhen sets the retry logic.
But what if we want to set retry times and retry logic?
OK, let’s look at the retryWhen operator first. This operator will stop retries if Error or Completed is raised internally and will pass the Error or Completed to the subscribe operator. Let’s go to the Demo first, which will help us understand:
const source = Rx.Observable.interval(1000)
const example = source.map(val= > {
if (val === 2) {
throw Error('error')}return val;
}).retryWhen(err= > {
return err
.scan((acc, curr) = > {
if (acc > 2) {
throw curr
}
return acc + 1
}, 1)
})
example.subscribe({
next: val= > console.log(val),
error: val= > console.log(val.message)
});
Copy the code
The online operation
The result is shown below:
The send logic has not changed, but there is a new operator: scan, so what does this operator do?
Scan can be thought of as the reduce function in javascript. This operator takes two arguments, the first a callback function and the second a default value. On the second retry, acc is 2. On the third retry, ACC is 3, which is already greater than 2. Then the if expression will be true and throw a curr directly. As mentioned above, if an Error is raised in scan, the retry will be stopped and given to subscribe, which then raises the Error function of the subscription and prints an Error.
In fact, after the number of retries, it is normal to throw the error out again, and let the following operators handle the error. However, some people may have a business requirement to return Completed, so you can refer to the following code:
const source = Rx.Observable.interval(200)
const example = source.map(val= > {
if (val === 2) {
throw Error('error')}return val;
}).retryWhen(err= > {
return err
.scan((acc, curr) = > {
return acc + 1
}, 0)
.takeWhile(v= > v <= 2)
})
example.subscribe({
complete: (a)= > console.log('Completed'),
next: val= > console.log(val),
error: val= > console.log(val.message)
});
Copy the code
The online operation
The running results are shown as follows:
You can see that a new operator, takeWhile, is used. This operator accepts a function that returns true and passes the value to the following operator. Once the function returns false, complete in SUBSCRIBE is triggered, that is, the sequence is complete. If you look at it this way, you can see the intent of the above code.
Resolving the Promise problem
Retry when does not support promise.reject (). A Promise does not have a retry API, and the Promise is already running when it is retried, so it cannot be called again. This causes retry and retryWhen to fail to retry promises. So the solution is simple.
We can use the defer operator, but let’s briefly explain what it does.
Defer accepts a function argument that will not run, but only if you subscribe. And we run the function in a separate run space, which means that even if we use Promise, we’re not going to be able to retry, because instead of reusing the previous result, we’re going to reopen a new memory space to run the function and return the result.
So we can write the code like this:
const getInfo: AxiosPromise = axios.get('http://xxx.com')
const exp = defer((a)= > getInfo)
.retryWhen(err= > {
return err.scan((acc, curr) = > {
if (acc > 2) {
throw curr
}
return acc + 1
}, 1)
})
example.subscribe({
next: val= > console.log(val),
error: val= > console.log(val.message)
});
Copy the code
The author information
Black-Hole: [email protected]
Blog: www.bugs.cc
Github: github.com/BlackHole1
other
Division I (Philharmonic) recruitment, interested partners can cast resume ah.
Flexible working, fruit of the day, nice colleagues, 965, group construction, five insurances and one housing fund…
Location: Shanghai Pusoft Building