我发现很少使用take(1)
的Auth Guards实现。在我的项目中,我使用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)
}
}
答案 0 :(得分:104)
运营商first()
和take()
不一样。
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
答案 1 :(得分:9)
似乎在RxJS 5.2.0中.first()
运算符有bug,
由于该错误.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
的链接答案 2 :(得分:9)
first()
如果排放量为零,并且您未明确处理(使用catchError
),则该错误将被传播,可能在其他地方引起意外问题,并且很难追踪-特别是在来自最终用户。
在大多数情况下,使用take(1)
更安全,前提是:
take(1)
如果源不发射就完成不发射任何东西。 first(x => x > 10)
)注意:您可以 可以将带有take(1)
的谓词用于.pipe( filter(x => x > 10), take(1) )
。如果没有大于10,则没有错误。
single()
如果您想更严格,并且禁止两次排放,则可以使用single()
,如果排放量为零或2+,则会出错。同样,在这种情况下,您需要处理错误。
提示:Single
有时会很有用,如果您想确保可观察的链不会做额外的工作,例如两次调用http服务并发出两个可观察的对象。在管道的末尾添加single
将使您知道是否犯了这样的错误。我在“任务运行器”中使用它,您在其中传递了一个仅可发出一个值的可观察任务,因此我通过single(), catchError()
传递了响应,以确保行为良好。
first()
而不是take(1)
吗? aka。 first
如何潜在地引起更多错误?
如果您有一个可观察对象,它从服务中获取某些东西,然后将其通过first()
进行管道传输,那么大多数时候应该会很好。但是,如果有人出于某种原因来停用该服务-并将其更改为发出of(null)
或NEVER
,则任何下游first()
操作员都会开始抛出错误。
现在,我意识到这可能正是您想要的东西-因此,为什么这只是个提示。运算符first
之所以吸引我,是因为它听起来比take(1)
的“笨拙”要少一些,但是如果有可能源不发光,则需要谨慎处理错误。完全取决于您在做什么。
还请考虑.pipe(defaultIfEmpty(42), first())
,如果您有默认值,则在不发出任何内容时应使用该默认值。当然,这不会引发错误,因为first
总是会收到一个值。
请注意,defaultIfEmpty
仅在流为空时才触发,而如果发出的值为null
则不会触发。
答案 3 :(得分:8)
有一个非常重要的区别,在任何地方都没有提到。
take(1)发出1,完成,取消订阅
first()发出1,完成,但没有取消订阅。
这意味着你的上游observable在first()之后仍然会很热,这可能不是预期的行为。
UPD:这是对RxJS 5.2.0的引用。这个问题可能已经修复。
答案 4 :(得分:2)
以下是三个带有大理石图的可观察对象A
,B
和C
,以探讨first
,take
和{{1}之间的区别}运算符:
* 传奇:
single
值
--o--
错误
----!
完成
在 https://thinkrx.io/rxjs/first-vs-take-vs-single/ 上播放。
已经有了所有答案,我想添加一个更直观的解释
希望它对某人有帮助
答案 5 :(得分:1)
事实证明,这两种方法之间有一个非常重要的区别:如果流在发出值之前完成,则 first()将发出错误。或者,如果您提供了谓词(i.e. 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的新手,这种行为使我非常困惑,尽管这是我自己的错,因为我做出了一些错误的假设。如果我不愿检查文档,就会发现行为是clearly documented:
如果未提供
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);
}
}
该代码起初看起来是无害的,但是当stream$
之前被销毁的组件可以发出值时,就会出现问题。因为我使用的是first()
,所以销毁组件时会引发错误。我通常只订阅流以获取要在组件内使用的值,因此我不在乎该组件在流发出之前是否已被销毁。因此,我几乎在以前使用过take(1)
的所有地方都开始使用first()
。
filter(fn).take(1)
比first(fn)
更加冗长,但是在大多数情况下,我更喜欢冗长而不是处理最终不会对应用程序造成影响的错误。
还要注意的一点:last()
和takeLast(1)
也是一样。