(1) - ilk ()


137

Ben bir kaç uygulama buldum AuthGuardkullanan s take(1). Projemde kullandım first().

İkisi de aynı şekilde mi çalışıyor?

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

Yanıtlar:


198

Operatörler first()ve take(1)aynı değil.

first()Operatör, isteğe bağlı alır predicatefonksiyonu ve bir yayan errorkaynak tamamlandığında bir değer eşleşen bildirim.

Örneğin bu bir hata verir:

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

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

... bunun kadar iyi:

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

Bu, yayılan ilk değerle eşleşecek olsa da:

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

Öte yandan take(1)sadece ilk değeri alır ve tamamlar. Başka bir mantık söz konusu değildir.

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

Gözlemlenebilir boş kaynak ile herhangi bir hata yaymaz:

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

Oca 2019: RxJS 6 için güncellendi


2
Aynen not olarak, bunu söylemedim first()ve take()genel olarak aynı, bence açık, sadece bu first()ve take(1)aynı. Hala bir fark olduğunu düşünüyorsan cevabından emin değilim?
Günter Zöchbauer

14
@ GünterZöchbauer Aslında davranışları farklı. Kaynak hiçbir şey yaymaz ve tamamlarsa, hiçbir şey yaymazken first()hata bildirimi gönderin take(1).
martin

@martin, bazı durumlarda take (1) hiçbir şey yayınlamaz, kodun hata ayıklamasının daha zor olacağını söylemek anlamına gelir mi?
Karuban

7
@Karuban Bu gerçekten sizin kullanım alanınıza bağlı. Herhangi bir değer almamak, kullanmanızı önerdiğimden beklenmedik bir durumdur first(). Geçerli bir uygulama durumuysa, giderim take(1).
martin

2
Bu .NET en benzer .First()vs .FirstOrDefault()de düşünmek (ve gelecek .Take(1)Birinci koleksiyonunda şey gerektirdiğini ve boş koleksiyonu için bir hata veriyor - ve hem FirstOrDefault()ve .Take(1)toplama boş ve dönüş olmasını sağlayacaktır nullsırasıyla ve boş koleksiyonu.
Simon_Weaver

45

İpucu: Yalnızca şu durumlarda kullanın first():

  • Yayılan sıfır öğeyi bir hata koşulu olarak kabul edersiniz (örneğin, yaymadan önce tamamlama) VE % 0'dan daha fazla hata olasılığı varsa, onu nazikçe ele alırsınız
  • VEYA Gözlemlenebilir olan kaynağın 1'den fazla öğe yayacağını% 100 biliyorsunuz (bu nedenle asla atamazsınız) .

Sıfır emisyon varsa ve bunu açıkça ele almıyorsanız (ile catchError), o zaman bu hata yayılır, muhtemelen başka bir yerde beklenmedik bir soruna neden olur ve izlenmesi oldukça zor olabilir - özellikle de bir son kullanıcıdan geliyorsa.

Sen daha güvenli kullanarak kapalı take(1)olması koşuluyla çoğunlukla:

  • İle Sen TAMAM take(1)kaynak emisyon olmadan tamamlarsa şey yayan değil.
  • Satır içi bir yüklem kullanmanıza gerek yoktur (örn. first(x => x > 10))

Not: edebilir bir yüklemi take(1)böyle: .pipe( filter(x => x > 10), take(1) ). Hiçbir şey 10'dan büyük değilse bunda bir hata yoktur.

Ne dersin single()

Daha da katı olmak ve iki emisyona izin vermemek istiyorsanız single(), sıfır veya 2+ emisyon varsa hangi hataları kullanabilirsiniz . Yine bu durumda hataları halletmeniz gerekir.

İpucu: SingleGözlenebilir zincirinizin bir http hizmetini iki kez çağırıp iki gözlemlenebilir şey yaymak gibi fazladan işler yapmamasını sağlamak istiyorsanız, ara sıra yararlı olabilir. singleBorunun sonuna eklemek , böyle bir hata yapıp yapmadığınızı size bildirir. Bunu, yalnızca bir değer yayması gereken gözlemlenebilir bir görevi geçtiğiniz bir 'görev çalıştırıcı'da kullanıyorum, bu yüzden single(), catchError()iyi davranışı garanti etmek için yanıtı iletiyorum.


Neden her zaman first()yerine kullanmıyorsunuz take(1)?

diğer adıyla. Nasıl first potansiyel olarak daha fazla hataya neden olabilir?

Bir servisten bir şey alan ve daha sonra onu first()sizden geçiren bir gözlemlenebiliriniz varsa, çoğu zaman iyi olur. Ancak biri herhangi bir nedenle hizmeti devre dışı bırakmak için gelirse - ve onu yayacak şekilde değiştirirse of(null)veya NEVERdaha sonra herhangi bir alt first()operatör hata vermeye başlar.

Şimdi anlıyorum ki tam olarak istediğiniz şey bu olabilir - bu yüzden bu sadece bir ipucu. Operatör firstbana hitap etti çünkü kulağa biraz daha az 'sakar' geliyordu, take(1)ancak kaynağın yayılmama şansı varsa, hataları ele alma konusunda dikkatli olmalısınız. Tamamen ne yaptığınıza bağlı olacaktır.


Varsayılan bir değeriniz varsa (sabit):

Ayrıca .pipe(defaultIfEmpty(42), first()), hiçbir şey yayınlanmadığında kullanılması gereken varsayılan bir değeriniz olup olmadığını da düşünün . Bu elbette bir hata oluşturmaz çünkü firsther zaman bir değer alır.

Bunun defaultIfEmptyyalnızca akış boş olduğunda tetikleneceğini, yayılan değerin değeri olmadığında tetiklenmeyeceğini unutmayın null.


Unutmayın singledaha farklılıkları vardır first. 1. Yalnızca değeri yayacaktır complete. Bu, eğer gözlemlenebilir bir değer yayarsa ancak hiçbir zaman tamamlanmazsa, o zaman single'ın asla bir değer yaymayacağı anlamına gelir. 2. Herhangi bir nedenden ötürü, singlehiçbir şeyle eşleşmeyen bir filtre işlevi undefinedgeçirirseniz, orijinal sıra boş değilse, durum böyle değilse, bir değer yayar first.
Marinos bir

28

İşte üç Gözlenebilirler vardır A, Bve Cmermer şemaları ile arasındaki farkı keşfetmek için first, takeve singleoperatörler:

ilk ve alış - tek operatör karşılaştırması

* Açıklamalar :
--o-- değer
----! hatası
----| tamamlama

Https://thinkrx.io/rxjs/first-vs-take-vs-single/ adresinde onunla oynayın .

Zaten tüm cevapları aldım, daha görsel bir açıklama eklemek istedim

Umarım birine yardımcı olur


12

Hiçbir yerde belirtilmeyen gerçekten önemli bir fark var.

take (1) 1 yayınlar, tamamlar, abonelikten çıkarır

first () 1 yayınlar, tamamlar ancak abonelikten çıkmaz.

Bu, yukarı akış gözlemlenebilirinizin ilk () sonrasında hala sıcak olacağı anlamına gelir ve bu muhtemelen beklenmeyen bir davranıştır.

UPD: Bu, RxJS 5.2.0'a atıfta bulunur. Bu sorun zaten düzeltilmiş olabilir.



10
Evet, her iki operatör de aboneliği tamamlar, fark hata işlemede olur. Bu gözlemlenebilir değer yaymazsa ve yine de ilk işleci kullanarak ilk değeri almaya çalışırsa bir hata atar. Abonelik gerçekleştiğinde akışta değer olmasa da take (1) operatörü ile değiştirirsek hata vermez.
noelyahan

7
Açıklığa kavuşturmak için: her ikisi de abonelikten çıkın. @Weltschmerz'den alınan örnek çok basitleştirilmişti, kendi kendine abonelikten çıkıncaya kadar çalışmıyor. Bu biraz daha genişletilmiş: repl.it/repls/FrayedHugeAudacity
Stephan LV

10

Görünüşe göre RxJS 5.2.0'da .first()operatörün bir hatası var ,

Bu hata nedeniyle .take(1)ve .first()aşağıdakilerle kullanıyorsanız oldukça farklı davranabilir switchMap:

İle take(1)davranışını alacak beklendiği gibi:

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...

Ama .first()seninle yanlış davranacaksın:

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... 

İşte codepen bağlantısı

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.