Cách xử lý lỗi từ toán tử bản đồ RxJS (góc)


93

Tôi muốn loại bỏ lỗi từ toán tử bản đồ có thể quan sát của tôi dựa trên một điều kiện. Ví dụ: nếu không nhận được dữ liệu API chính xác. Vui lòng xem đoạn mã sau:

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

Về cơ bản như bạn có thể thấy trong mã, nếu phản hồi (đối tượng res) không có 'bearerToken', tôi muốn loại bỏ lỗi. Vì vậy, trong đăng ký của tôi, nó đi vào tham số thứ 2 (handleError) được đề cập bên dưới.

.subscribe(success, handleError)

Bất kỳ đề xuất?


4
Về throw 'Valid token not returned';thì sao?
Günter Zöchbauer

Thất bại để biên dịch
Hassan

Xin vui lòng thông báo lỗi chính xác.
Günter Zöchbauer

2
Xin lỗi, nó không hoạt động với return throw 'message here'nhưng không hoạt động mà không có returntừ khóa. Hãy để tôi kiểm tra xem nó có hoạt động đúng logic không.
Hassan

Văn bản lỗi không được nhận trong subscribephương thức và .finally()trong luồng cũng kích hoạt. (Tuy nhiên, việc thực hiện bị dừng lại, đó là một điều tốt)
Hassan

Câu trả lời:


141

Chỉ cần ném lỗi vào bên trong map()nhà điều hành. Tất cả các lệnh gọi lại trong RxJS đều được bao bọc bằng các khối try-catch, vì vậy nó sẽ bị bắt và sau đó được gửi dưới dạng errorthông báo.

Điều này có nghĩa là bạn không trả lại bất kỳ thứ gì và chỉ cần gặp lỗi:

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

Các throwError()(cựu Observable.throw()trong RxJS 5) là một Quan sát rằng chỉ cần gửi một errorthông báo nhưng map()không quan tâm những gì bạn quay trở lại. Ngay cả khi bạn trả về một Observable từ map()nó, nó sẽ được chuyển dưới dạng nextthông báo.

Điều cuối cùng, bạn có thể không cần sử dụng .catchError()(trước đây catch()trong RxJS 5). Nếu bạn cần thực hiện bất kỳ tác dụng phụ nào khi xảy ra lỗi, tốt hơn nên sử dụng tap(null, err => console.log(err))(trước đây do()trong RxJS 5) chẳng hạn.

Tháng 1 năm 2019: Cập nhật cho RxJS 6


1
Cảm ơn @martin - Có giải pháp của bạn hoạt động. Tôi thực sự đã gặp sự cố bên trong phương thức logError của mình mà @ GünterZöchbauer đã chỉ ra. Tôi đã phải returnđối tượng lỗi từ nó và bây giờ nó hoạt động hoàn hảo :) Cảm ơn!
Hassan

@martin: Bạn có thể vui lòng cho biết lý do tại sao chúng tôi không muốn bạn .catch () ở đây?
Bob

1
@ Bob Bởi vì OP đã được sử dụng catch()chỉ để đăng nhập và rethrow lỗi, mà không cần thiết nếu bạn chỉ muốn thực hiện một tác dụng phụ (đăng nhập lỗi) và nó dễ dàng hơn để sử dụng chỉdo()
martin

1
Điều này có giống với return throwError(new Error('Valid token not returned'));?
Simon_Weaver

@Simon_Weaver không, không. return throwError()trả về một Observable<never>, điều này chỉ làm gián đoạn luồng có thể quan sát ngay lập tức, mà không trở lại.
Phục hồi Monica

25

Nếu bạn cảm thấy throw new Error()có vẻ như không thể quan sát được, bạn có thể sử dụng throwError(...)với switchMapthay vì map(sự khác biệt switchMaptrả về một giá trị quan sát mới):

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

hay ngắn gọn hơn:

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

Hành vi sẽ giống nhau, nó chỉ là cú pháp khác nhau.

Theo nghĩa đen, bạn đang nói 'chuyển' từ http có thể quan sát được trong đường ống sang một địa chỉ có thể quan sát khác, chỉ là 'gói' giá trị đầu ra hoặc một 'lỗi' mới có thể quan sát được.

Đừng quên đặt ofnếu không bạn sẽ nhận được một số thông báo lỗi khó hiểu.

Ngoài ra, vẻ đẹp của 'switchMap' là bạn có thể trả về một 'chuỗi' lệnh hoàn toàn mới nếu bạn muốn - cho bất kỳ logic nào cần được thực hiện saveJwt.


4
Khi tôi bắt đầu nghĩ về switchMapmột ifcâu lệnh không đồng bộ - mọi thứ có ý nghĩa hơn rất nhiều :-)
Simon_Weaver

3

Mặc dù câu hỏi này đã được trả lời, tôi muốn chia sẻ cách tiếp cận của riêng tôi (mặc dù nó chỉ hơi khác so với ở trên).

Tôi sẽ quyết định những gì được trả về tách biệt với ánh xạ và ngược lại. Tôi không chắc nhà điều hành nào là tốt nhất cho việc này nên tôi sẽ sử dụng tap.

this.httpPost.pipe(
  tap(res => { 
    if (!res.bearerToken) {
      throw new Error('Valid token not returned');
    }
  }),
  map(res => this.saveJwt(res.bearerToken)),
);

giá trị trả về của tapbị bỏ qua. mã này làm điều khác với nó nói
sf

Tôi vẫn đang quen với rxjs. Sử dụng switchMap sẽ tốt hơn? Ai đó có thể đề xuất một toán tử khác hoặc chỉnh sửa trực tiếp không?
christo8989

Tôi nghĩ rằng đề xuất đó throw new Error()là lựa chọn tốt nhất cho đến nay
sf
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.