take(1)対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)同じですが。あなたがまだ違いがあると思うかどうかあなたの答えから私はわかりませんか?
ギュンターZöchbauer

14
@GünterZöchbauer実際、彼らの行動は異なります。ソースが何も出力せずに完了した場合first()、エラー通知を送信しますが、何も出力しませんtake(1)
火曜日

@martin、場合によっては、take(1)が何も出力しないので、コードのデバッグがより困難になるということですか?
カルバン2017

7
@カルバンこれは本当にあなたのユースケースに依存します。値を受け取っていない場合は、使用することをお勧めしますが予想外ですfirst()。それが有効なアプリケーション状態である場合、私はに進みtake(1)ます。
火曜日

2
これは、.NETのに似ている.First().FirstOrDefault()(とも考えてみると.Take(1)まず、コレクションに何かを必要とし、空のコレクションのためのエラーを与えることに-との両方FirstOrDefault().Take(1)コレクションが空と復帰することができnull、それぞれ、空のコレクション。
Simon_Weaver

44

ヒント:次のfirst()場合にのみ使用してください:

  • あなたは、エラー条件(例えば発光する前に完了)であると放出さゼロの項目を検討する、エラーの0%の確率よりも大きいがあります場合は、正常にそれを扱います
  • または、ソースオブザーバブルが1個以上のアイテムを放出する(したがって、スローできない)ことを100%知っています。

排出量がゼロであり、明示的に(を使用してcatchError)処理していない場合、そのエラーは伝搬し、予期しない問題が他の場所で発生する可能性があり、特にエンドユーザーからの場合は追跡が非常に難しい場合があります。

次の条件が満たされてtake(1)いれば、ほとんどの部分を安全に使用できます。

  • take(1)ソースが放出なしで完了した場合、何も放出しなくても問題ありません。
  • あなたはインライン述語を使用する必要はありません(例。first(x => x > 10)

注:述語は次のように使用できます。10を超えるものがない場合でも、エラーは発生しません。take(1).pipe( filter(x => x > 10), take(1) )

どうですか single()

さらに厳格になり、2つの放出を許可しないsingle()場合は、放出ゼロまたは2以上の場合にどのエラーを使用できます。この場合も、エラーを処理する必要があります。

ヒント:Singleオブザーバブルチェーンがhttpサービスを2回呼び出して2つのオブザーバブルを発行するなどの余分な作業を行わないようにしたい場合に役立つことがあります。singleパイプの最後に追加すると、そのような間違いをした場合に通知されます。1つの値のみを放出するはずのタスクオブザーバブルを渡す「タスクランナー」で使用しているので、single(), catchError()適切な動作を保証するために応答を渡します。


first()代わりに常に使用しないのはなぜtake(1)ですか?

別名。エラーが発生するfirst 可能性はありますか?

サービスから何かを取得してそれをパイプするオブザーバブルがある場合、first()ほとんどの場合問題ありません。しかし、場合、誰かが何らかの理由でサービスを無効に沿って来る-と発光するように、それを変更しof(null)たりNEVER、その後の任意の下流first()事業者がエラーを投げ始めるでしょう。

今、私はそれがまさにあなたが望むものかもしれないことを理解しています-それがなぜこれが単なるヒントなのか。first「不器用」に聞こえる音が少なかったため、オペレーターは私にアピールしましたtake(1)が、ソースが発生しない可能性がある場合は、エラーの処理に注意する必要があります。あなたがやっていることに完全に依存します。


デフォルト値(定数)がある場合:

.pipe(defaultIfEmpty(42), first())何も出力されない場合に使用するデフォルト値があるかどうかも検討してください。もちろん、first常に値を受け取るため、これによってエラーが発生することはありません。

defaultIfEmptyストリームが空の場合にのみトリガーされ、出力されるものの値がの場合にはトリガーされないことに注意してくださいnull


とのsingle違いがもっとあることに注意してくださいfirst1.で値を出力するだけcompleteです。これは、オブザーバブルが値を出力するが完了しない場合、singleが値を出力しないことを意味します。2.何らかの理由で、single何にも一致しないフィルター関数を渡すと、undefined元のシーケンスが空でない場合に値が出力されfirstます。これはには当てはまりません。
マリノス

28

ここでは3観測されているABC大理石の違いを探るための図で firsttake、およびsingle演算子は:

最初の演算子とテイク演算子と単一演算子の比較

* 凡例
--o--
----! エラー
----| 完了

https://thinkrx.io/rxjs/first-vs-take-vs-single/試してください

すでにすべての答えを持っているので、もっと視覚的な説明を追加したかった

それが誰かを助けることを願って


12

どこにも述べられていない本当に重要な違いが1つあります。

take(1)は1、完了、サブスクライブ解除を発行します

first()は1を発行し、完了しますが、サブスクライブを解除しません。

これは、上流のオブザーバブルがfirst()の後もまだ高温であることを意味します。これはおそらく予期しない動作です。

UPD:これはRxJS 5.2.0を指します。この問題はすでに修正されている可能性があります。


どちらも退会しないと思います。jsbin.com/ nuzulorota / 1 / edit?jsconsoleを参照してください。
weltschmerz 2017

10
はい、両方のオペレーターがサブスクリプションを完了します。違いはエラー処理で発生します。そのオブザーバブルが値を出力せず、最初の演算子を使用して最初の値を取得しようとすると、エラーがスローされます。サブスクリプションが発生したときに値がストリームに存在しない場合でも、それをtake(1)演算子で置き換えると、エラーはスローされません。
noelyahan 2017

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.