observables - Angular 2 usando RxJS-take(1) vs first()
que es un observable en angular (5)
Consejo: Use solo
first()
si:
- Considera que cero elementos emitidos son una condición de error (por ejemplo, completar antes de emitir) Y si hay una probabilidad mayor de 0% de error, lo maneja con gracia
- O bien, Usted sabe al 100% que la fuente observable emitirá 1 o más elementos (por lo que nunca puede arrojar) .
Si hay cero emisiones y no lo está manejando explícitamente (con
catchError
), ese error se propagará, posiblemente causará un problema inesperado en otro lugar y puede ser bastante difícil de rastrear, especialmente si proviene de un usuario final.
Estás
más seguro
usando
take(1)
en su mayor parte siempre que:
-
Estás de acuerdo con que
take(1)
no emita nada si la fuente se completa sin una emisión. -
No necesita usar un predicado en línea (por ejemplo,
first(x => x > 10)
)
Nota: Puede usar un predicado con
take(1)
como este:
.pipe( filter(x => x > 10), take(1) )
.
No hay ningún error con esto si nada es mayor que 10.
¿Qué pasa con
single()
Si desea ser aún más estricto y no permitir dos emisiones, puede usar
single()
que errores si hay
cero o más de 2 emisiones
.
Nuevamente, necesitaría manejar los errores en ese caso.
Consejo:
Single
puede ocasionalmente ser útil si desea asegurarse de que su cadena observable no esté haciendo un trabajo adicional, como llamar a un servicio http dos veces y emitir dos observables.
Agregar
single
al final de la tubería le permitirá saber si cometió tal error.
Lo estoy usando en un ''corredor de tareas'' donde pasas una tarea observable que solo debería emitir un valor, así que paso la respuesta a través de
single(), catchError()
para garantizar un buen comportamiento.
¿Por qué no usar siempre
first()
lugar de
take(1)
?
aka. ¿Cómo puede causar potencialmente más errores?
Si tiene un observable que toma algo de un servicio y luego lo canaliza
first()
, debería estar bien la mayor parte del tiempo.
Pero si alguien viene a deshabilitar el servicio por cualquier razón, y lo cambia para emitir
of(null)
o
NEVER
, cualquier operador
first()
descendente comenzaría a arrojar errores.
Ahora me doy cuenta de que puede ser
exactamente
lo que quieres, de ahí que esto sea solo un consejo.
El operador
first
me atrajo porque sonaba un poco menos `` torpe '''' que
take(1)
pero debe tener cuidado con el manejo de errores si alguna vez existe la posibilidad de que la fuente no se emita.
Sin embargo, dependerá completamente de lo que estés haciendo.
Si tiene un valor predeterminado (constante):
Considere también
.pipe(defaultIfEmpty(42), first())
si tiene un valor predeterminado que debe usarse si no se emite nada.
Por supuesto, esto no generaría un error porque
first
siempre recibiría un valor.
Tenga en cuenta que
defaultIfEmpty
solo se activa si la secuencia está vacía, no si el valor de lo que se emite es
null
.
Encontré poca implementación de Auth Guards que usa
take(1)
.
En mi proyecto, usé
first()
para satisfacer mis necesidades.
¿Funciona de la misma manera?
O uno de ellos podría tener ventajas más o menos.
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)
}
}
Aquí hay tres observables
A
,
B
y
C
con diagramas de mármol para explorar la diferencia entre los operadores
first
,
take
y
single
:
*
Leyenda
:
--o--
valor
----!
error
----|
terminación
Juega con él en https://thinkrx.io/rxjs/first-vs-take-vs-single/ .
Ya teniendo todas las respuestas, quería agregar una explicación más visual.
Espero que ayude a alguien
Hay una diferencia realmente importante que no se menciona en ninguna parte.
take (1) emite 1, completa, cancela la suscripción
first () emite 1, completa, pero no se da de baja.
Significa que su observable ascendente seguirá caliente después de first (), lo que probablemente no sea un comportamiento esperado.
UPD: Esto se refiere a RxJS 5.2.0. Este problema podría estar ya solucionado.
Los operadores
first()
y
take(1)
no son lo mismo.
El
first()
operador
first()
toma una función de
predicate
opcional y emite una notificación de
error
cuando ningún valor coincide cuando se completa la fuente.
Por ejemplo, esto emitirá un error:
import { EMPTY, range } from ''rxjs'';
import { first, take } from ''rxjs/operators'';
EMPTY.pipe(
first(),
).subscribe(console.log, err => console.log(''Error'', err));
... tan bien como esto:
range(1, 5).pipe(
first(val => val > 6),
).subscribe(console.log, err => console.log(''Error'', err));
Si bien esto coincidirá con el primer valor emitido:
range(1, 5).pipe(
first(),
).subscribe(console.log, err => console.log(''Error'', err));
Por otro lado,
take(1)
solo toma el primer valor y completa.
No hay más lógica involucrada.
range(1, 5).pipe(
take(1),
).subscribe(console.log, err => console.log(''Error'', err));
Luego, con la fuente vacía Observable, no emitirá ningún error:
EMPTY.pipe(
take(1),
).subscribe(console.log, err => console.log(''Error'', err));
Enero de 2019: actualizado para RxJS 6
Parece que en RxJS 5.2.0 el operador .first
.first()
tiene un
bug
,
Debido a ese error,
.take(1)
y
.first()
pueden comportarse de manera bastante diferente si los está usando con
switchMap
:
Con
take(1)
obtendrá el comportamiento esperado:
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...
Pero con
.first()
obtendrá un comportamiento incorrecto:
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...
Aquí hay un enlace a codepen