Como lançar o erro do operador de mapa RxJS (angular)

93

Quero lançar um erro do operador de mapa do meu observável com base em uma condição. Por exemplo, se os dados API corretos não forem recebidos. Por favor, veja o seguinte código:

private userAuthenticate( email: string, password: string ) {
    return this.httpPost(`${this.baseApiUrl}/auth?format=json&provider=login`, {userName: email, password: password})
        .map( res => { 
            if ( res.bearerToken ) {
                return this.saveJwt(res.bearerToken); 
            } else {
                // THIS DOESN'T THROW ERROR --------------------
                return Observable.throw('Valid token not returned');
            }
        })
        .catch( err => Observable.throw(this.logError(err) )
        .finally( () => console.log("Authentication done.") );
}

Basicamente, como você pode ver no código, se a resposta (objeto res) não tiver 'bearerToken', quero exibir um erro. Para que na minha assinatura vá para o segundo parâmetro (handleError) mencionado abaixo.

.subscribe(success, handleError)

Alguma sugestão?

Hassan
fonte
4
Sobre o quê throw 'Valid token not returned';?
Günter Zöchbauer
Falha ao compilar
Hassan
Mensagem de erro exata, por favor.
Günter Zöchbauer
2
Desculpe, não funciona com return throw 'message here'a returnpalavra - chave, mas funciona sem . Deixe-me verificar se está funcionando corretamente logicamente.
Hassan
O texto do erro não está sendo recebido no subscribemétodo e .finally()no fluxo também é acionado. (No entanto, a execução é interrompida, o que é uma coisa boa)
Hassan

Respostas:

141

Basta jogar o erro dentro do map()operador. Todos os retornos de chamada em RxJS são agrupados com blocos try-catch para que sejam capturados e enviados como uma errornotificação.

Isso significa que você não retorna nada e apenas lança o erro:

map(res => { 
  if (res.bearerToken) {
    return this.saveJwt(res.bearerToken); 
  } else {
    throw new Error('Valid token not returned');
  }
})

o throwError() (antigo Observable.throw()em RxJS 5) é um Observable que apenas envia uma errornotificação, mas map()não se importa com o que você retorna. Mesmo se você retornar um Observável map()dele, será passado como uma nextnotificação.

Por último, você provavelmente não precisa usar .catchError()(antigo catch()em RxJS 5). Se você precisar realizar algum efeito colateral quando ocorrer um erro, é melhor usar tap(null, err => console.log(err))(antigo do()em RxJS 5), por exemplo.

Janeiro de 2019: Atualizado para RxJS 6

Martin
fonte
1
Obrigado @martin - Sim, sua solução funciona. Na verdade, eu também tive um problema dentro do meu método logError, que @ GünterZöchbauer apontou. Eu tive que returno objeto de erro dele e agora funciona perfeitamente :) Obrigado!
Hassan
@martin: Você poderia desenvolver por que não gostaríamos de ver você .catch () aqui?
Bob
1
@Bob Porque o OP estava usando catch()apenas para registrar e relançar o erro, o que é desnecessário se você quiser apenas executar um efeito colateral (registrar o erro) e é mais fácil de usar apenasdo()
martin
1
Isso é idêntico a return throwError(new Error('Valid token not returned'));?
Simon_Weaver
@Simon_Weaver não, não é. return throwError()retorna um Observable<never>, isso apenas interrompe o fluxo observável imediatamente, sem retornar de forma alguma.
Reintegrar Monica em
25

Se você sentir que throw new Error()parece não observável, você pode usar throwError(...)com em switchMapvez de map(a diferença sendo switchMapretorna um novo observável):

// this is the import needed for throwError()
import { throwError } from 'rxjs';


// RxJS 6+ syntax
this.httpPost.pipe(switchMap(res => { 
   if (res.bearerToken) {
      return of(this.saveJwt(res.bearerToken)); 
   } 
   else {
      return throwError('Valid token not returned');  // this is 
   }
});

ou mais concisamente:

this.httpPost.pipe(switchMap(res => (res.bearerToken) ? 
                                    of(this.saveJwt(res.bearerToken)) : 
                                    throwError('Valid token not returned')
));

O comportamento será o mesmo, é apenas uma sintaxe diferente.

Você está literalmente dizendo 'mudar' do http observável no tubo para um observável diferente, que está apenas 'envolvendo' o valor de saída ou um novo 'erro' observável.

Não se esqueça de colocar ofou você receberá algumas mensagens de erro confusas.

Além disso, a beleza de 'switchMap' é que você pode retornar uma nova 'cadeia' de comandos se quiser - para qualquer lógica que precise ser feita saveJwt.

Simon_Weaver
fonte
4
Uma vez que comecei a pensar switchMapem uma ifinstrução assíncrona - as coisas fizeram muito mais sentido :-)
Simon_Weaver
3

Mesmo que essa pergunta já tenha sido respondida, eu gostaria de compartilhar minha própria abordagem (embora seja apenas ligeiramente diferente da anterior).

Eu decidiria o que é retornado separado do mapeamento e vice-versa. Não tenho certeza de qual operadora é melhor para isso, então vou usar tap.

this.httpPost.pipe(
  tap(res => { 
    if (!res.bearerToken) {
      throw new Error('Valid token not returned');
    }
  }),
  map(res => this.saveJwt(res.bearerToken)),
);
christo8989
fonte
o valor de retorno de tapé ignorado. este código faz uma coisa diferente do que diz
sf
Ainda estou me acostumando com rxjs. Usar switchMap seria melhor? Alguém pode sugerir um operador diferente ou editar diretamente?
christo8989
Acho que sugerido throw new Error()é a melhor opção até agora
sf