รับ (1) vs แรก ()


137

ผมพบว่าการดำเนินการไม่กี่วินาทีที่ใช้AuthGuard take(1)ในโครงการของฉันฉันใช้first().

ทั้งสองอย่างทำงานเหมือนกันหรือไม่?

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/first';
import { Observable } from 'rxjs/Observable';

import { Injectable } from '@angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AngularFire } from 'angularfire2';

@Injectable()
export class AuthGuard implements CanActivate {

    constructor(private angularFire: AngularFire, private router: Router) { }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
        return this.angularFire.auth.map(
            (auth) =>  {
                if (auth) {
                    this.router.navigate(['/dashboard']);
                    return false;
                } else {
                    return true;
                }
            }
        ).first(); // Just change this to .take(1)
    }
}

คำตอบ:


198

ตัวดำเนินการfirst()และtake(1)ไม่เหมือนกัน

ตัวfirst()ดำเนินการรับpredicateฟังก์ชั่นเสริมและส่งเสียงerrorแจ้งเตือนเมื่อไม่มีค่าที่ตรงกันเมื่อต้นทางเสร็จสมบูรณ์

ตัวอย่างเช่นสิ่งนี้จะแสดงข้อผิดพลาด:

import { EMPTY, range } from 'rxjs';
import { first, take } from 'rxjs/operators';

EMPTY.pipe(
  first(),
).subscribe(console.log, err => console.log('Error', err));

... เช่นเดียวกับสิ่งนี้:

range(1, 5).pipe(
  first(val => val > 6),
).subscribe(console.log, err => console.log('Error', err));

แม้ว่าจะตรงกับค่าแรกที่ปล่อยออกมา:

range(1, 5).pipe(
  first(),
).subscribe(console.log, err => console.log('Error', err));

ในทางกลับกันtake(1)เพียงแค่ใช้ค่าแรกและทำให้เสร็จสมบูรณ์ ไม่มีตรรกะเพิ่มเติมที่เกี่ยวข้อง

range(1, 5).pipe(
  take(1),
).subscribe(console.log, err => console.log('Error', err));

จากนั้นด้วยแหล่งว่างที่สังเกตได้จะไม่แสดงข้อผิดพลาดใด ๆ :

EMPTY.pipe(
  take(1),
).subscribe(console.log, err => console.log('Error', err));

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


2
ฉันไม่ได้พูดแบบนั้นfirst()และtake()เหมือนกันโดยทั่วไปซึ่งฉันคิดว่าชัดเจนเพียงอย่างเดียวfirst()และtake(1)เหมือนกัน ฉันไม่แน่ใจจากคำตอบของคุณว่าคุณคิดว่ายังมีความแตกต่างอยู่หรือไม่?
GünterZöchbauer

14
@ GünterZöchbauerจริงๆแล้วพฤติกรรมของพวกเขาแตกต่างกัน หากแหล่งที่มาไม่ปล่อยสิ่งใดออกมาและดำเนินการเสร็จสิ้นให้first()ส่งการแจ้งเตือนข้อผิดพลาดในขณะที่take(1)ไม่ปล่อยอะไรเลย
martin

@martin ในบางกรณีใช้เวลา (1) จะไม่ส่งออกมาหมายความว่าการดีบักโค้ดจะยากขึ้นหรือไม่?
Karuban

7
@ Karuban อันนี้ขึ้นอยู่กับ usecase ของคุณจริงๆ หากไม่ได้รับค่าใด ๆ first()ที่ไม่คาดคิดเป็นกว่าผมขอแนะนำให้ไปใช้งานที่ take(1)ถ้ามันเป็นรัฐแอพลิเคชันที่ถูกต้องฉันไปกับ
martin

2
สิ่งนี้คล้ายกับ. NET's .First()vs .FirstOrDefault()(และลองคิดดู.Take(1)ด้วยว่า First ต้องการบางสิ่งบางอย่างในคอลเลกชันและให้ข้อผิดพลาดสำหรับคอลเล็กชันว่าง - และทั้งสองอย่างFirstOrDefault()และ.Take(1)อนุญาตให้คอลเลกชันว่างเปล่าและส่งคืนnullและคอลเล็กชันว่างตามลำดับ
Simon_Weaver

45

เคล็ดลับ: ใช้เฉพาะfirst()เมื่อ:

  • คุณถือว่ารายการที่ปล่อยออกมาเป็นศูนย์เป็นเงื่อนไขข้อผิดพลาด (เช่นกรอกข้อมูลก่อนที่จะปล่อย) และหากมีโอกาสผิดพลาดมากกว่า 0% คุณจะจัดการได้อย่างสง่างาม
  • หรือคุณจะรู้ว่า 100% ที่สังเกตแหล่งที่จะปล่อย 1+ รายการ (ดังนั้นไม่สามารถโยน)

หากไม่มีการปล่อยมลพิษเป็นศูนย์และคุณไม่ได้จัดการอย่างชัดเจน (ด้วยcatchError) ข้อผิดพลาดนั้นจะแพร่กระจายไปอาจทำให้เกิดปัญหาที่ไม่คาดคิดที่อื่นและอาจเป็นเรื่องยุ่งยากในการติดตามโดยเฉพาะอย่างยิ่งหากมาจากผู้ใช้ปลายทาง

คุณปลอดภัยกว่าโดยใช้take(1)เป็นส่วนใหญ่โดยมีเงื่อนไขว่า:

  • คุณตกลงที่จะtake(1)ไม่ปล่อยอะไรออกมาหากแหล่งที่มาสมบูรณ์โดยไม่มีการปล่อย
  • คุณไม่จำเป็นต้องใช้เพรดิเคตแบบอินไลน์ (เช่นfirst(x => x > 10))

หมายเหตุ: คุณสามารถใช้คำกริยาที่มีเช่นนี้take(1) .pipe( filter(x => x > 10), take(1) )จะไม่มีข้อผิดพลาดหากไม่มีอะไรมากกว่า 10

เกี่ยวกับ single()

หากคุณต้องการที่จะเข้มงวดและไม่อนุญาตให้มีการปล่อยสองคุณสามารถใช้single()ซึ่งข้อผิดพลาดถ้ามีศูนย์หรือ 2+ ปล่อยก๊าซเรือนกระจก อีกครั้งคุณต้องจัดการข้อผิดพลาดในกรณีนั้น

เคล็ดลับ: ในSingleบางครั้งอาจมีประโยชน์หากคุณต้องการให้แน่ใจว่าห่วงโซ่ที่สังเกตได้ของคุณไม่ได้ทำงานพิเศษเช่นการเรียกใช้บริการ http สองครั้งและปล่อยสัญญาณที่สังเกตได้สองครั้ง การเพิ่มsingleปลายท่อจะทำให้คุณทราบว่าคุณทำผิดพลาดดังกล่าวหรือไม่ ฉันใช้มันใน 'นักวิ่งงาน' ซึ่งคุณผ่านงานที่สังเกตได้ซึ่งควรจะปล่อยออกมาเพียงค่าเดียวดังนั้นฉันจึงส่งคำตอบผ่านsingle(), catchError()เพื่อรับประกันพฤติกรรมที่ดี


ทำไมไม่ใช้first()แทนเสมอtake(1)?

อาคา วิธีการสามารถfirst ที่อาจก่อให้เกิดข้อผิดพลาดขึ้น?

หากคุณมีสิ่งที่สังเกตได้ซึ่งนำบางสิ่งมาจากบริการแล้วต่อท่อผ่าน first()คุณก็น่าจะดีเกือบตลอดเวลา แต่ถ้ามีคนมาปิดใช้บริการไม่ว่าด้วยเหตุผลใดก็ตาม - และเปลี่ยนเป็นปล่อยมิof(null)ฉะนั้นผู้ให้บริการNEVERดาวน์สตรีมfirst()จะเริ่มแสดงข้อผิดพลาด

ตอนนี้ผมตระหนักดีว่าอาจจะตรงกับสิ่งที่คุณต้องการ - เพราะฉะนั้นทำไมนี้เป็นเพียงปลาย ตัวดำเนินการfirstสนใจฉันเพราะฟังดู 'เงอะงะ' น้อยกว่าเล็กน้อยtake(1)แต่คุณต้องระมัดระวังเกี่ยวกับการจัดการข้อผิดพลาดหากมีโอกาสที่แหล่งที่มาจะไม่เปล่งออกมา ทั้งหมดจะขึ้นอยู่กับสิ่งที่คุณกำลังทำอยู่


หากคุณมีค่าเริ่มต้น (ค่าคงที่):

พิจารณาด้วย .pipe(defaultIfEmpty(42), first())คุณมีค่าเริ่มต้นที่ควรใช้หากไม่มีการปล่อยออกมา แน่นอนว่านี่จะไม่ทำให้เกิดข้อผิดพลาดเพราะfirstจะได้รับค่าเสมอ

โปรดทราบว่าจะถูกเรียกเฉพาะในกรณีที่กระแสว่างเปล่าไม่ได้ถ้าค่าของสิ่งที่ถูกปล่อยออกมาคือdefaultIfEmptynull


โปรดทราบว่ามีความแตกต่างมากขึ้นเพื่อsingle 1.มันจะปล่อยค่าบน ซึ่งหมายความว่าหากสิ่งที่สังเกตได้ปล่อยค่าออกมา แต่ไม่เสร็จสมบูรณ์ single ก็จะไม่ปล่อยค่าออกมา 2.ด้วยเหตุผลบางประการหากคุณส่งผ่านฟังก์ชันตัวกรองซึ่งไม่ตรงกับสิ่งใด ๆ มันจะปล่อยค่าออกมาหากลำดับดั้งเดิมไม่ว่างเปล่าซึ่งไม่เป็นเช่นนั้น firstcompletesingleundefinedfirst
Marinos

28

ที่นี่สาม observables มีA, BและCมีไดอะแกรมหินอ่อนในการสำรวจความแตกต่างระหว่าง first, takeและsingleผู้ประกอบการ:

ครั้งแรกเทียบกับการเปรียบเทียบกับตัวดำเนินการเดี่ยว

* คำอธิบาย :
--o-- ค่า
----! ผิดพลาดที่
----| เสร็จสมบูรณ์

เล่นกับมันได้ที่ https://thinkrx.io/rxjs/first-vs-take-vs-single/

มีคำตอบทั้งหมดแล้วฉันต้องการเพิ่มคำอธิบายที่เป็นภาพมากขึ้น

หวังว่ามันจะช่วยใครบางคน


12

มีข้อแตกต่างที่สำคัญอย่างหนึ่งซึ่งไม่ได้กล่าวถึงที่ใด

รับ (1) ปล่อย 1 เสร็จสมบูรณ์ยกเลิกการสมัคร

แรก () ส่งเสียง 1 เสร็จสิ้น แต่ไม่ยกเลิกการสมัคร

หมายความว่าต้นน้ำที่สังเกตได้ของคุณจะยังคงร้อนหลังจากก่อน () ซึ่งอาจเป็นพฤติกรรมที่ไม่คาดคิด

UPD: สิ่งนี้อ้างอิงถึง RxJS 5.2.0 ปัญหานี้อาจได้รับการแก้ไขแล้ว


ฉันไม่คิดว่าคนใดคนหนึ่งการยกเลิกการสมัครดูjsbin.com/nuzulorota/1/edit?js,console
weltschmerz

10
ใช่ผู้ดำเนินการทั้งสองดำเนินการสมัครสมาชิกให้เสร็จสิ้นความแตกต่างเกิดขึ้นในการจัดการข้อผิดพลาด หากสิ่งที่สังเกตได้นั้นไม่ปล่อยค่าออกมาและยังคงพยายามรับค่าแรกโดยใช้ตัวดำเนินการตัวแรกจะทำให้เกิดข้อผิดพลาด หากเราแทนที่ด้วยตัวดำเนินการ take (1) แม้ว่าค่าจะไม่มีในสตรีมเมื่อการสมัครสมาชิกเกิดขึ้นก็จะไม่เกิดข้อผิดพลาด
noelyahan

7
เพื่อชี้แจง: ทั้งสองทำการยกเลิกการสมัคร ตัวอย่างจาก @weltschmerz ง่ายเกินไปมันจะไม่ทำงานจนกว่าจะยกเลิกการสมัครด้วยตัวเอง อันนี้ขยายได้อีกเล็กน้อย: repl.it/repls/FrayedHugeAudacity
Stephan LV

10

ดูเหมือนว่าใน RxJS 5.2.0 ไฟล์ .first()ดำเนินการมีจุดบกพร่อง ,

เพราะจุดบกพร่องนั้น .take(1).first()นั้นและสามารถทำงานได้ค่อนข้างแตกต่างกันหากคุณใช้กับswitchMap :

กับ take(1)คุณจะได้รับพฤติกรรมตามที่คาด:

var x = Rx.Observable.interval(1000)
   .do( x=> console.log("One"))
   .take(1)
   .switchMap(x => Rx.Observable.interval(1000))
   .do( x=> console.log("Two"))
   .subscribe((x) => {})

// In the console you will see:
// One
// Two
// Two
// Two
// Two
// etc...

แต่ด้วย .first()คุณจะมีพฤติกรรมผิด ๆ :

var x = Rx.Observable.interval(1000)
  .do( x=> console.log("One"))
  .first()
  .switchMap(x => Rx.Observable.interval(1000))
  .do( x=> console.log("Two"))
  .subscribe((x) => {})

// In console you will see:
// One
// One
// Two
// One
// Two
// One
// etc... 

นี่คือลิงค์ไปยังcodepen

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