我发现了一些使用take(1)的AuthGuards实现。在我的项目中,我使用了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)
    }
}

当前回答

在RxJS 5.2.0中,.first()操作符似乎有一个错误,

因为这个错误,如果你在switchMap中使用.take(1)和.first(),它们的行为会有很大的不同:

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

这是codeen的链接

其他回答

提示:仅在以下情况下使用first():

您认为发出的零项是错误条件(例如。如果出错的几率大于0%,你就优雅地处理它 或者你100%知道源可观察对象将发射1+项(所以永远不会抛出)。

如果零排放,并且您没有显式地处理它(使用catchError),那么错误将被传播,可能会在其他地方引起意外的问题,并且可能相当棘手—特别是如果它来自最终用户。

在大多数情况下,使用take(1)会更安全,前提是:

如果源完成时没有发射,那么take(1)不发射任何东西也没问题。 您不需要使用内联谓词(例如。第一个(x => x > 10))

注意:您可以像这样使用take(1)的谓词:.pipe(filter(x => x > 10), take(1))。如果没有比10更大的数,这就没有错误。

那么single()呢?

如果你想更严格,不允许两次释放,你可以使用single(),如果有0或2+释放,它会出错。同样,在这种情况下,您需要处理错误。

提示:如果你想确保你的可观察对象链没有做额外的工作,比如调用http服务两次并发出两个可观察对象,那么Single偶尔会有用。将single添加到管道的末尾将让您知道是否犯了这样的错误。我在一个“任务运行器”中使用它,在那里你传递一个任务可观察到的,应该只发出一个值,所以我通过single(), catchError()传递响应,以保证良好的行为。


为什么不总是使用first()而不是take(1) ?

又名。首先如何可能导致更多错误?

如果你有一个可观察对象,它从服务中获取一些东西,然后通过管道first()传递它,那么大多数时候应该没问题。但是如果有人出于某种原因禁用了该服务——并将其更改为发出(null)或NEVER,那么任何下游的first()操作符都会开始抛出错误。

现在我意识到这可能正是你想要的——因此这只是一个技巧。操作符首先吸引了我,因为它听起来没有take(1)那么“笨拙”,但如果有可能源不发射,你需要小心处理错误。这完全取决于你在做什么。


如果你有一个默认值(常量):

如果您有一个默认值,则在没有触发时使用该值,那么还要考虑.pipe(defaultIfEmpty(42), first())。这当然不会引发错误,因为first总是会接收到一个值。

注意,只有在流为空时才会触发defaultIfEmpty,而不是在发出的值为空时才会触发。

以下是三个可观察对象A、B和C,并带有大理石图,以探索first、take和单个操作符之间的区别:

*传奇: —o—价值 ——!错误 ——|完成

在https://thinkrx.io/rxjs/first-vs-take-vs-single/上玩吧。

已经有了所有的答案,我想添加一个更直观的解释

希望它能帮助到别人

操作符first()和take(1)是不同的。

first()操作符接受一个可选的谓词函数,并在源完成时没有匹配值时发出错误通知。

例如,这将发出一个错误:

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更新

有一个非常重要的区别,在任何地方都没有提到。

Take(1)生成1,完成,取消订阅

First()触发1,完成,但不取消订阅。

这意味着你的上游可观察对象在first()之后仍然是热的,这可能不是预期的行为。

UPD:这是指RxJS 5.2.0。这个问题可能已经解决了。

在RxJS 5.2.0中,.first()操作符似乎有一个错误,

因为这个错误,如果你在switchMap中使用.take(1)和.first(),它们的行为会有很大的不同:

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

这是codeen的链接