Search code examples
swifttimerrx-swiftretry-logic

Why is take(1) used at the end of a retryWhen timer?


I'm looking reading from RW book.

.retryWhen { errors in
    return errors.enumerated().flatMap { (attempt, error) -> Observable<Int> in
        if attempt >= maxAttempts - 1 {
            return Observable.error(error)
        }
        return Observable<Int>.timer(Double(attempt + 1), scheduler:
            MainScheduler.instance).take(1)
    }
}

The timer isn’t taking a period Variable so it’s firing once and is not repeating. So why is it doing take(1). I see that happening over a few times in the course of the book.


Solution

  • The take(1) is not necessary here. take(1) would have made sure that the timer doesn't repeat.

    The Observable.timer is an operator that emits a value periodically. Unless the period parameter is still nil, in which case a TimerOneOffSink would be created. A TimerOneOffSink emits one element and then completes and gets disposed of.

    For example:

    Observable<Int>
        .timer(3.0,
               scheduler: MainScheduler.instance)
        .take(10)
        .subscribe(
            onNext:      { print($0) },
            onCompleted: { print("Completed") },
            onDisposed:  { print("Disposed") }
        )
    

    would print:

    0
    Completed
    Disposed
    

    After consulting with Marin Todorov, one of the authors, he confirmed that it must be just an oversight.

    The whole idea of the code snippet is to wait between retries using a timer with a duration increasing by 1 second: the result is an incremental back-off strategy with a maximum number of attempts.