Cara melempar kesalahan dari operator peta RxJS (sudut)

93

Saya ingin melempar kesalahan dari operator peta yang dapat saya amati berdasarkan suatu kondisi. Misalnya jika data API yang benar tidak diterima. Silakan lihat kode berikut:

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.") );
}

Pada dasarnya seperti yang Anda lihat di kode, jika respons (objek res) tidak memiliki 'bearerToken', saya ingin mengeluarkan kesalahan. Sehingga dalam langganan saya ini masuk ke parameter ke-2 (handleError) yang disebutkan di bawah ini.

.subscribe(success, handleError)

Ada saran?

Hassan
sumber
4
Tentang apa throw 'Valid token not returned';?
Günter Zöchbauer
Gagal untuk dikompilasi
Hassan
Mohon pesan kesalahannya tepat.
Günter Zöchbauer
2
Oh maaf, ini tidak berfungsi return throw 'message here'tetapi berfungsi tanpa returnkata kunci. Biarkan saya memeriksa apakah itu berfungsi dengan benar secara logis.
Hassan
Teks kesalahan tidak diterima dalam subscribemetode dan .finally()dalam aliran juga terpicu. (Namun eksekusi dihentikan yang merupakan hal yang baik)
Hassan

Jawaban:

141

Buang saja kesalahan di dalam map()operator. Semua callback di RxJS dibungkus dengan blok coba-tangkap sehingga akan ditangkap dan kemudian dikirim sebagai errornotifikasi.

Ini berarti Anda tidak mengembalikan apa pun dan hanya membuang kesalahan:

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

The throwError()(mantan Observable.throw()di RxJS 5) adalah Observable yang hanya mengirim errorpemberitahuan tetapi map()tidak peduli apa yang Anda kembalikan. Meskipun Anda mengembalikan Observable map()darinya akan diteruskan sebagai nextnotifikasi.

Hal terakhir, Anda mungkin tidak perlu menggunakan .catchError()(sebelumnya catch()di RxJS 5). Jika Anda perlu melakukan efek samping ketika terjadi kesalahan, lebih baik menggunakan tap(null, err => console.log(err))(sebelumnya do()di RxJS 5) misalnya.

Jan 2019: Diperbarui untuk RxJS 6

martin
sumber
1
Terima kasih @martin - Ya, solusi Anda berhasil. Saya sebenarnya memiliki masalah di dalam metode logError saya juga yang ditunjukkan oleh @ GünterZöchbauer. Saya harus returnobjek kesalahan darinya dan sekarang berfungsi dengan sempurna :) Terima kasih!
Hassan
@martin: Bisakah Anda menjelaskan mengapa kami tidak menginginkan Anda .catch () di sini?
Bob
1
@Bob Karena OP catch()hanya menggunakan untuk mencatat dan mengembalikan kesalahan, yang tidak perlu jika Anda hanya ingin melakukan efek samping (mencatat kesalahan) dan lebih mudah menggunakannyado()
martin
1
Apakah ini identik dengan return throwError(new Error('Valid token not returned'));?
Simon_Weaver
@Simon_Weaver tidak, bukan itu. return throwError()mengembalikan sebuah Observable<never>, ini hanya mengganggu aliran yang dapat diamati dengan segera, tanpa kembali sama sekali.
Kembalikan Monica
25

Jika Anda merasa seperti throw new Error()tidak dapat diamati-seperti Anda dapat menggunakan throwError(...)dengan switchMapalih - alih map(perbedaannya switchMapmengembalikan yang dapat diamati baru):

// 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 
   }
});

atau lebih tepatnya:

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

Perilakunya akan sama, hanya sintaks yang berbeda.

Anda benar-benar mengatakan 'beralih' dari http yang dapat diamati di pipa ke yang dapat diamati yang berbeda, yang hanya 'membungkus' nilai output, atau 'kesalahan' baru yang dapat diamati.

Jangan lupa untuk meletakkan ofatau Anda akan mendapatkan beberapa pesan kesalahan yang membingungkan.

Juga keindahan 'switchMap' adalah Anda dapat mengembalikan 'rantai' perintah yang sama sekali baru jika Anda mau - untuk logika apa pun yang perlu dilakukan saveJwt.

Simon_Weaver
sumber
4
Setelah saya mulai berpikir switchMapsebagai ifpernyataan asinkron - semuanya menjadi lebih masuk akal :-)
Simon_Weaver
3

Meskipun pertanyaan ini sudah terjawab, saya ingin berbagi pendekatan saya sendiri (walaupun hanya sedikit berbeda dari yang di atas).

Saya akan memutuskan apa yang dikembalikan terpisah dari pemetaan dan sebaliknya. Saya tidak yakin operator mana yang terbaik untuk ini jadi saya akan gunakan tap.

this.httpPost.pipe(
  tap(res => { 
    if (!res.bearerToken) {
      throw new Error('Valid token not returned');
    }
  }),
  map(res => this.saveJwt(res.bearerToken)),
);
christo8989
sumber
nilai kembalian tapdiabaikan. kode ini melakukan hal yang berbeda dari yang dikatakan
sf
Saya masih terbiasa dengan rxjs. Apakah menggunakan switchMap lebih baik? Dapatkah seseorang menyarankan operator lain atau mengedit secara langsung?
christo8989
Saya pikir yang disarankan throw new Error()adalah opsi terbaik sejauh ini
sf