我发现了一些使用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)
    }
}

当前回答

操作符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更新

其他回答

操作符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。这个问题可能已经解决了。

事实证明,这两个方法之间有一个非常重要的区别:如果流在发出值之前完成,first()将发出一个错误。或者,如果你已经提供了一个谓词(即first(value => value === 'foo')),如果流在通过谓词的值被触发之前完成,它将发出一个错误。

另一方面,如果一个值从未从流中发出,Take(1)将愉快地继续执行。这里有一个简单的例子:

const subject$ = new Subject();

// logs "no elements in sequence" when the subject completes
subject$.first().subscribe(null, (err) => console.log(err.message));

// never does anything
subject$.take(1).subscribe(console.log);

subject$.complete();

另一个例子,使用一个谓词:

const observable$ = of(1, 2, 3);

// logs "no elements in sequence" when the observable completes
observable$
 .first((value) => value > 5)
 .subscribe(null, (err) => console.log(err.message));

// the above can also be written like this, and will never do
// anything because the filter predicate will never return true
observable$
 .filter((value) => value > 5);
 .take(1)
 .subscribe(console.log);

作为RxJS的新手,这种行为让我非常困惑,尽管这是我自己的错,因为我做了一些不正确的假设。如果我费心去检查文档,我就会看到这种行为被清楚地记录下来:

如果未提供defaultValue且未找到匹配的元素,则抛出错误。

我经常遇到这种情况的原因是Angular 2中一个相当常见的模式,即在OnDestroy生命周期钩子期间手动清理可观察对象:

class MyComponent implements OnInit, OnDestroy {
  private stream$: Subject = someDelayedStream();
  private destroy$ = new Subject();

  ngOnInit() {
    this.stream$
      .takeUntil(this.destroy$)
      .first()
      .subscribe(doSomething);
  }

  ngOnDestroy() {
    this.destroy$.next(true);
  }
}

代码一开始看起来无害,但当组件在流$发出值之前被销毁时,问题就出现了。因为我使用的是first(),所以当组件被销毁时会抛出一个错误。我通常只订阅流以获得将在组件中使用的值,所以我不关心组件是否在流发出之前被销毁。正因为如此,我开始在几乎所有以前使用take(1)的地方使用take(1)。

filter(fn).take(1)比first(fn)更冗长,但在大多数情况下,我更喜欢处理一些最终对应用程序没有影响的错误。

同样需要注意的是:这同样适用于last()和takeLast(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的链接

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

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

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

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

希望它能帮助到别人