Как использовать Rxjs switchMap для throwError

Я создал оболочку для функции извлечения. Исходя из приведенного ниже кода, test_3 пройден, но почему test_1 и test_2 ударили по успешному обратному вызову вместо ошибочного обратного вызова? Я подозреваю, что я использую throwError, что-то не так.

import { from, throwError } from 'rxjs' // version 6.5.2
import { retry, catchError, switchMap } from 'rxjs/operators'

function getBody(response: Response): Promise<any> {
  const headers = response.headers;

  if (headers.has('content-type')) {
    const contentType: string = headers.get('content-type');
    if (contentType.includes('json')) return response.json();
  }
  return response.text();
}

const http = (url) => 
  from(fetch(new Request(url))
    .pipe(
      retry(3),
      catchError(error => { // fetch will throw error if page not found.
        console.log('hit catchError')
        return of(new Response(null, { status: 404, statusText: 'Page not found' }));
      }),
      switchMap(async (response: Response) => {
        console.log('response.ok = ', response.ok);
        return response.ok
          ? getBody(response) // all status >= 200 and < 400 
          : throwError({ 
              status: response.status, 
              statusText: response.statusText, 
              body: await getBody(response) 
            });
      }),
    );

// test_1
http('http://this_url_not_exists.com').subscribe(
  response => console.log('should not hit this'),
  errorResponse => console.log('should errorResponse.status = 404'),
);
// test_1 console result:
// hit catchError
// response.ok = false
// should not hit this

// test_2
http('http://this_url_require_authentication.com').subscribe(
  response => console.log('should not hit this'),
  errorResponse => console.log('should errorResponse.status = 401'),
);
// test_2 console result:
// response.ok = false
// should not hit this

// test_3
http('http://myurl.com').subscribe(
  response => console.log('should hit this'),
  errorResponse => console.log('should not hit this'),
);
// test_3 console result:
// response.ok = true
// should hit this

Пожалуйста, не предлагайте мне использовать ajax rxjs.

Всего 2 ответа


fetch не выдаст за вас ошибку

https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

Обещание, возвращаемое функцией fetch (), не будет отклонять состояние ошибки HTTP, даже если ответом является HTTP 404 или 500. Вместо этого он будет разрешен в обычном режиме (при условии, что для состояния ok установлено значение false), и он будет отклоняться только при сбое сети или если что-то помешало выполнению запроса.

Обновление: похоже, ваш вызов API возвращает 401 и будет отклонен в обещании получения, но вы все еще не можете полагаться на получение для правильного отклонения. пожалуйста, смотрите ниже тему

https://github.com/github/fetch/issues/201

и в отношении вашего кода причина, по которой он не обрабатывается switchMap, заключается в том, что вы возвращаете throwError, который не является обещанием (вы помечаете функцию как async)

замените throwError(...) на throwError().toPromise() будет работать правильно. но опять же не полагайтесь на fetch для правильного отклонения


Удалите async файл из вашей switchMap и верните наблюдаемое с помощью of .

const http = (url) => 
  from(fetch(new Request(url))
    .pipe(
      retry(3),
      catchError(error => { // fetch will throw error if page not found.
        return of(new Response(null, { status: 404, statusText: 'Page not found' }));
      }),
      switchMap((response: Response) =>        
        response.ok
          ? of(response) // return an observable here using 'of'
          : throwError(response)
      ),
    );

switchMap заставляет switchMap возвращать Observable того, что вы возвращаете. Таким образом, в вашем switchMap если response.ok == false вы вернули Observable<Observable<never>> который затем отправил Observable<never> в ваш успешный обратный вызов.


Есть идеи?

10000