Обработка ошибок самовосстановления в RxJS

Я новичок в RxJS.

У меня есть обещанный базовый HTTP-клиент. У него есть метод login(), который извлекает токен с сервера и сохраняет его внутри для использования другими методами.

По истечении срока действия токена сервер ответит ошибкой HTTP 401, и в этом случае я хочу снова вызвать login() и повторить запрос. Также повторите попытку для других типов ошибок.

Я не уверен, как смоделировать это в RxJS, у меня есть что-то работающее, но я надеюсь, что есть лучший способ сделать это.

(это внутри декоратора, который применяется ко всем методам, требующим аутентификации, здесь wrappedFunc)

        return interval(100)
            .pipe(
                startWith(0),
                switchMap(() => {
                    return wrappedFunc.apply(this, args)
                }),
                retryWhen(errors$ => errors$.pipe(
                    switchMap((error) => {
                        if (error.response.status === 401) {
                            return this.login()
                        } else {
                            return of(error)
                        }
                    }),
                    scan((attempts, currentError) => {
                        if (attempts > 5) {
                            throw currentError
                        }
                        return attempts + 1
                    }, 0),
                )),
                first()
            )

Каков идиоматический способ обработки ошибок в RxJS?


person miedwar    schedule 21.08.2018    source источник
comment
Почему interval(100)? Это задерживает вызов wrappedFunc?   -  person m1ch4ls    schedule 21.08.2018
comment
Упс! Это повторять каждые 100 мс, у меня сложилось впечатление, что он будет излучаться немедленно, а затем ждать 100 мс и снова излучать, но, глядя на документы, кажется, я ошибался! :) Добавление startWith(0), кажется, исправляет это.   -  person miedwar    schedule 21.08.2018


Ответы (1)


interval(100) излучает каждые 100 мс, а switchMap отменит текущую подписку и повторно подпишется, поэтому, если вызов wrappedFunc займет более 100 мс, он никогда не «произойдет» - он будет пытаться выполнить вызов снова и снова.

Я думаю, вы хотите что-то еще в этом духе:

return defer(() => wrappedFunc.apply(this, args))
  .pipe(
    retryWhen(errors$ => errors$.pipe(
      switchMap((error) => {
        if (error.response.status === 401) {
          return this.login()
        } else {
          return of(error)
        }
      }),
      scan((attempts, currentError) => {
        if (attempts > 5) {
          throw currentError
        }
        return attempts + 1
      }, 0),
      delay(100),
    ))
  );

defer здесь служит оболочкой, поэтому wrappedFunc может возвращать либо Promise, либо Observable, а delay предназначен для задержки повторной попытки.

person m1ch4ls    schedule 21.08.2018