วิธีโยนข้อผิดพลาดจากตัวดำเนินการแผนที่ RxJS (เชิงมุม)


93

ฉันต้องการโยนข้อผิดพลาดจากตัวดำเนินการแผนที่ที่สังเกตได้ตามเงื่อนไข ตัวอย่างเช่นหากไม่ได้รับข้อมูล API ที่ถูกต้อง โปรดดูรหัสต่อไปนี้:

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

โดยทั่วไปอย่างที่คุณเห็นในโค้ดหากการตอบสนอง (วัตถุ res) ไม่มี 'bearerToken' ฉันต้องการโยนข้อผิดพลาดออกไป ดังนั้นในการสมัครสมาชิกของฉันมันจะเข้าสู่พารามิเตอร์ที่ 2 (handleError) ที่กล่าวถึงด้านล่าง

.subscribe(success, handleError)

ข้อเสนอแนะใด ๆ ?


4
เกี่ยวกับอะไรthrow 'Valid token not returned';?
GünterZöchbauer

ไม่สามารถรวบรวม
Hassan

ข้อความแสดงข้อผิดพลาดที่แน่นอนโปรด
GünterZöchbauer

2
ขออภัยมันใช้ไม่ได้return throw 'message here'แต่ใช้งานได้หากไม่มีreturnคีย์เวิร์ด ให้ฉันตรวจสอบว่ามันทำงานถูกต้องตามเหตุผลหรือไม่
Hassan

ไม่ได้รับข้อความแสดงข้อผิดพลาดในsubscribeเมธอดนี้และ.finally()ในสตรีมจะทริกเกอร์ด้วย (อย่างไรก็ตามการประหารชีวิตหยุดลงซึ่งเป็นสิ่งที่ดี)
ฮัสซัน

คำตอบ:


141

เพียงแค่โยนข้อผิดพลาดภายในตัวmap()ดำเนินการ การโทรกลับทั้งหมดใน RxJS ถูกห่อหุ้มด้วยบล็อกการลองจับดังนั้นมันจะถูกจับแล้วส่งเป็นการerrorแจ้งเตือน

ซึ่งหมายความว่าคุณจะไม่ส่งคืนอะไรเลยและเพียงแค่โยนข้อผิดพลาด

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

throwError()(อดีตObservable.throw()ใน RxJS 5) เป็นผู้สังเกตว่าเพียงแค่ส่งerrorการแจ้งเตือน แต่map()ไม่สนใจสิ่งที่คุณกลับมา แม้ว่าคุณจะส่งคืน Observable จากmap()มันก็จะถูกส่งต่อเป็นการnextแจ้งเตือน

สิ่งสุดท้ายคุณอาจไม่จำเป็นต้องใช้.catchError()(เดิมcatch()ใน RxJS 5) หากคุณต้องการทำผลข้างเคียงใด ๆ เมื่อเกิดข้อผิดพลาดควรใช้tap(null, err => console.log(err))(เดิมdo()ใน RxJS 5)

ม.ค. 2019: อัปเดตสำหรับ RxJS 6


1
ขอบคุณ @martin - ใช่โซลูชันของคุณใช้งานได้ จริงๆแล้วฉันมีปัญหาในเมธอด logError ของฉันเช่นกันซึ่ง @ GünterZöchbauerชี้ให้เห็น ฉันต้องreturnใช้วัตถุข้อผิดพลาดจากมันและตอนนี้มันก็ทำงานได้อย่างสมบูรณ์ :) ขอบคุณ!
Hassan

@martin: คุณช่วยพัฒนาว่าทำไมเราถึงไม่ต้องการคุณที่นี่ .catch () ที่นี่?
Bob

1
@Bob เนื่องจาก OP ใช้catch()เพื่อบันทึกและแก้ไขข้อผิดพลาดอีกครั้งซึ่งไม่จำเป็นหากคุณเพียงแค่ต้องการแสดงผลข้างเคียง (บันทึกข้อผิดพลาด) และใช้งานได้ง่ายกว่าdo()
มาร์ติน

1
สิ่งนี้เหมือนกับreturn throwError(new Error('Valid token not returned'));หรือไม่
Simon_Weaver

@Simon_Weaver ไม่มันไม่ใช่ return throwError()ส่งคืนค่าObservable<never>นี้จะขัดจังหวะสตรีมที่สังเกตได้ทันทีโดยไม่ส่งคืนเลย
คืนสถานะ Monica

25

ถ้าคุณรู้สึกว่าthrow new Error()ดูเหมือนว่าคุณสามารถใช้เช่นยกเลิกการสังเกต- throwError(...)กับswitchMapแทนmap(ความแตกต่างเป็นswitchMapผลตอบแทนที่สังเกตใหม่):

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

หรือกระชับกว่านี้:

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

ลักษณะการทำงานจะเหมือนกันเป็นเพียงไวยากรณ์ที่แตกต่างกัน

คุณกำลังพูดว่า 'เปลี่ยน' จาก http ที่สังเกตได้ในท่อไปเป็นค่าอื่นที่สังเกตได้ซึ่งอาจเป็นเพียงแค่ 'การตัด' ค่าผลลัพธ์หรือ 'ข้อผิดพลาด' ใหม่ที่สังเกตได้

อย่าลืมใส่ofไม่เช่นนั้นคุณจะได้รับข้อความแสดงข้อผิดพลาดที่ทำให้สับสน

นอกจากนี้ยังมีความงามของ 'switchMap' คือการที่คุณสามารถกลับมาใหม่ทั้งหมด 'โซ่' ของคำสั่งถ้าคุณต้องการ - saveJwtสำหรับสิ่งที่ต้องการตรรกะที่จะทำได้ด้วย


4
เมื่อผมเริ่มคิดว่าการswitchMapเป็นตรงกันifคำสั่ง - สิ่งที่ทำมากรู้สึกมากขึ้น :-)
Simon_Weaver

3

แม้ว่าคำถามนี้จะได้รับคำตอบแล้ว แต่ฉันก็อยากจะแบ่งปันแนวทางของตัวเอง (แม้ว่าจะแตกต่างจากข้างบนเล็กน้อย)

ฉันจะตัดสินใจว่าสิ่งที่ส่งคืนแยกต่างหากจากการทำแผนที่และในทางกลับกัน tapฉันไม่แน่ใจว่าสิ่งที่ผู้ประกอบการที่ดีที่สุดคือสำหรับการนี้เพื่อฉันจะใช้

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

ค่าส่งคืนของtapถูกละเว้น รหัสนี้แตกต่างจากที่กล่าวไว้
sf

ฉันยังคงชินกับ rxjs การใช้ switchMap จะดีกว่าไหม มีใครแนะนำตัวดำเนินการอื่นหรือแก้ไขโดยตรงได้ไหม
christo8989

ฉันคิดว่าคำแนะนำthrow new Error()นั้นเป็นตัวเลือกที่ดีที่สุด
sf
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.