Как использовать Rxjs switchMap для throwError - PullRequest
0 голосов
/ 15 мая 2019

Я создал оболочку для функции извлечения. Исходя из приведенного ниже кода, 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 ]

4 голосов
/ 15 мая 2019

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 для правильного отклонения

1 голос
/ 15 мая 2019

Удалите 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)
      ),
    );

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

...