take (1) vs first ()


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

답변:


197

연산자 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));

그런 다음 빈 소스 Observable을 사용하면 오류가 발생하지 않습니다.

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

2019 년 1 월 : RxJS 6 용으로 업데이트


2
그냥 참고로, 나는 그런 말을하지 않았다 first()그리고 take()난 단지 것이 분명하다 생각하는, 일반적으로 동일 first()take(1)동일는. 여전히 차이가 있다고 생각되면 귀하의 답변에서 확실하지 않습니까?
Günter Zöchbauer

14
@ GünterZöchbauer 사실, 그들의 행동은 다릅니다. 소스가 아무것도 방출하지 않고 완료되면 단순히 아무것도 방출하지 않으면 서 first()오류 알림 을 보냅니다 take(1).
martin

@ martin, 경우에 따라 take (1)은 코드 디버깅이 더 어려울 것이라고 말하는 것을 의미하지 않습니다.
Karuban

7
@Karuban 이것은 실제 사용 사례에 따라 다릅니다. 값을받지 못하면 예기치 않은 사용을 제안합니다 first(). 유효한 응용 프로그램 상태라면으로 갈 것입니다 take(1).
martin

2
이것은 .NET의 유사합니다 .First().FirstOrDefault()또한 생각하는 (그리고 오는 .Take(1)첫 번째 컬렉션에서 뭔가를 필요로한다는와 하늘의 콜렉션에 대한 오류를 제공합니다 - 모두 FirstOrDefault().Take(1)컬렉션이 비어 반환 될 수 있도록 null각각 빈 모음.
Simon_Weaver

45

팁 : 다음과 같은 first()경우 에만 사용 하십시오.

  • 당신은 제로 항목은 오류 상태로 방출 (예. 발광하기 전에 완료) 고려 AND 오류의 큰 0 % 이상 기회가 있다면 당신은 정상적으로 그것을 처리
  • 또는 100 %는 관측 가능한 소스가 1 개 이상의 아이템을 방출한다는 것을 알고 있습니다 .

배출량이 0이고 명시 적으로 처리하지 않으면 (으로 catchError) 해당 오류가 전파되어 다른 곳에서 예기치 않은 문제가 발생할 수 있으며 특히 최종 사용자가 올 경우 추적이 까다로울 수 있습니다.

다음 과 같은 경우 대부분 사용 하는 것이 안전take(1) 합니다.

  • 당신은 괜찮습니다 take(1) 소스가 방출없이 완료하면 아무것도 방출하지.
  • 당신은 인라인 술어를 사용할 필요가 없습니다 (예. first(x => x > 10))

참고 : 다음 과 같은 조건자를 사용할 수 있습니다take(1) ..pipe( filter(x => x > 10), take(1) ) . 10보다 큰 것이 없으면 오류가 없습니다.

이건 어떤가요 single()

더 엄격하고 두 가지 배출을 허용하지 않으려면 배출 single()0 또는 2 이상인 경우 어떤 오류를 사용할 수 있습니다 . 이 경우에도 오류를 처리해야합니다.

팁 : Single옵저버 블 체인이 http 서비스를 두 번 호출하고 두 개의 옵저버 블을 방출하는 등의 추가 작업을 수행하지 않도록하려면 때때로 유용 할 수 있습니다. single파이프 끝에 추가 하면 실수를 저지른 경우 알려줍니다. 하나의 값만 방출 해야하는 관찰 가능한 작업을 전달하는 '태스크 러너'에서 사용하고 있으므로 single(), catchError()좋은 동작을 보장하기 위해 응답을 전달합니다 .


왜 항상 first()대신에 사용 하지 않는가?take(1) 않습니까?

일명. 어떻게 first 잠재적으로 으로 더 많은 오류가 발생할 무엇입니까?

서비스에서 무언가를 가져 와서 파이프를 통해 관찰 할 수있는 관찰 가능 항목이 있다면 first()대부분의 시간에 문제가 없을 것입니다. 그러나 누군가가 어떤 이유로 든 서비스를 비활성화하기 위해 온다면-서비스를 방출 of(null)하거나 NEVER다운 스트림으로 변경하면first() 연산자는 오류를 던지기 시작합니다.

이제는 이것이 정확히 당신이 원하는 것일 수 있다는 것을 알고 있습니다 . 운영자 first는 '서투른'소리보다 약간 덜 들었 기 때문에 나에게 호소 take(1)했지만 소스가 방출되지 않을 가능성이있는 경우 오류 처리에주의해야합니다. 당신이하고있는 일에 전적으로 달려 있습니다.


기본값이있는 경우 (일정한) :

.pipe(defaultIfEmpty(42), first())아무 것도 방출되지 않으면 사용해야하는 기본값이 있는지도 고려하십시오 . 이것은 물론 오류를 일으키지 않습니다.first 항상 값을 받기 .

defaultIfEmpty에만 스트림이 비어있는 경우 트리거가 방출되는 것의 값이없는 경우 null.


single더 많은 차이점이 first있습니다. 1. 의 값만 방출합니다 complete. 이는 관찰 가능 값이 방출되지만 완료되지 않으면 단일 값이 방출되지 않음을 의미합니다. 2. 어떤 이유로 single일치하지 않는 필터 함수를 전달 undefined하면 원래 시퀀스가 ​​비어 있지 않은 경우 값 을 내 보냅니다 first.
마리노스

28

여기에 세 가지 Observables은있다 A, B그리고 C대리석 다이어그램의 차이를 탐구 first, take그리고 single운영자는 :

첫 대 대 연산자 비교

* 범례 :
--o--
----! 오류
----| 완료

https://thinkrx.io/rxjs/first-vs-take-vs-single/ 에서 함께 플레이 하십시오 .

이미 모든 답을 가지고 더 시각적 인 설명을 추가하고 싶었습니다.

누군가에게 도움이되기를 바랍니다.


12

어디에도 언급되지 않은 한 가지 중요한 차이점이 있습니다.

take (1)은 1을 방출하고 완료하고 구독을 취소합니다.

first ()는 1을 내고 완료하지만 구독 취소하지 않습니다.

이는 업스트림 관측 가능 항목이 first () 이후에도 여전히 뜨거워 예상되는 동작이 아님을 의미합니다.

UPD : RxJS 5.2.0을 나타냅니다. 이 문제는 이미 해결되었을 수 있습니다.


어느 쪽도 구독을 취소하지 않는다고 생각합니다 . jsbin.com/nuzulorota/1/edit?js,console을 참조하십시오 .
weltschmerz

10
예, 두 운영자 모두 구독을 완료하면 오류 처리에서 차이가 발생합니다. 해당 관찰 가능 값이 방출되지 않고 여전히 첫 번째 연산자를 사용하여 첫 번째 값을 가져 오려고하면 오류가 발생합니다. 구독이 발생할 때 스트림에 값이 없어도 take (1) 연산자로 바꾸면 오류가 발생하지 않습니다.
noelyahan

7
명확히하기 위해 : 둘 다 구독을 취소합니다. @weltschmerz의 예제는 너무 단순화되어 자체적으로 구독을 취소 할 수있을 때까지 실행되지 않습니다. :이 사람은 좀 더 확장 repl.it/repls/FrayedHugeAudacity
스테판 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.