example - return observable angular 4
Obtenga el valor actual de Observable sin suscribirse(solo desea valor una vez) (3)
El título lo dice todo de verdad. ¿Cómo puedo obtener el valor actual de un Observable sin suscribirme? Solo quiero el valor actual una vez y no valores nuevos, ya que están llegando ...
Necesitas usar BehaviorSubject ,
- BehaviorSubject es similar a ReplaySubject, excepto que solo recuerda la última publicación.
- BehaviorSubject también requiere que le proporcione un valor predeterminado de T. Esto significa que todos los suscriptores recibirán un valor inmediatamente (a menos que ya esté completado).
Le dará el valor más reciente publicado por el observable.
BehaviorSubject proporciona una propiedad getter
llamada value
para obtener el valor más reciente que se le pasa.
- En este ejemplo, el valor ''a'' se escribe en la consola:
//Declare a Subject, you''ll need to provide a default value.
var subject: BehaviorSubject<string> = new BehaviorSubject("a");
USO:
console.log(subject.value); // will print the current value
No estoy seguro si esto es lo que estás buscando. Probablemente escriba ese tema de comportamiento en un servicio. Declárelo como privado y exponga solo el valor que ha establecido. Algo como esto
@Injectable({
providedIn: ''root''
})
export class ConfigService {
constructor(private bname:BehaviorSubject<String>){
this.bname = new BehaviorSubject<String>("currentvalue");
}
getAsObservable(){
this.bname.asObservable();
}
}
De esta manera, los usuarios externos solo tienen acceso para suscribirse al comportamientoSujeto y puede establecer el valor deseado en el servicio.
Respuesta rápida:
... Solo quiero el valor actual una vez y no valores nuevos, ya que están llegando ...
Seguirá usando subscribe
, pero con pipe(take(1))
para que le dé un solo valor.
p.ej. myObs$.pipe(take(1)).subscribe(value => alert(value));
Consulte también: Comparación entre first()
, take(1)
o single()
Respuesta más larga:
La regla general es que solo debe obtener un valor de un observable con subscribe()
(o tubería asíncrona si se usa Angular)
BehaviorSubject
definitivamente tiene su lugar, y cuando comencé con RxJS solía hacer bs.value()
para obtener un valor. A medida que sus transmisiones RxJS se propagan a lo largo de toda su aplicación (¡y eso es lo que quiere!), Será cada vez más difícil hacerlo. A menudo, verás que .asObservable()
usa para "ocultar" el tipo subyacente para evitar que alguien use .value()
, y al principio esto parecerá malo, pero comenzarás a apreciar por qué se hace con el tiempo. Además, tarde o temprano necesitará un valor de algo que no sea un BehaviorSubject
y no habrá una manera de hacerlo así.
Sin embargo, volviendo a la pregunta original. Especialmente si no quieres ''hacer trampa'' usando un BehaviorSubject
.
El mejor enfoque es usar subscribe
para obtener un valor.
obs$.pipe(take(1)).subscribe(value => { ....... })
O
obs$.pipe(first()).subscribe(value => { ....... })
La diferencia entre estos dos first()
será un error si el flujo ya se ha completado y take(1)
no emitirá ningún observable si el flujo se ha completado o no tiene un valor disponible de inmediato.
Nota: esto se considera una mejor práctica incluso si está utilizando un BehaviorSubject.
Sin embargo, si prueba el código anterior, el "valor" del observable se "atorará" dentro del cierre de la función de suscripción y es posible que lo necesite en el ámbito actual. Una forma de evitar esto si realmente tienes que hacerlo es esta:
const obsValue = undefined;
const sub = obs$.pipe(take(1)).subscribe(value => obsValue = value);
sub.unsubscribe();
// we will only have a value here if it was IMMEDIATELY available
alert(obsValue);
Es importante tener en cuenta que la llamada de suscripción anterior no espera un valor. Si no hay nada disponible de inmediato, nunca se llamará a la función de suscripción, y coloco la llamada para cancelar la suscripción deliberadamente para evitar que ''aparezca más tarde''.
Así que no solo esto se ve muy torpe: no funcionará para algo que no está disponible de inmediato, como un valor de resultado de una llamada http, sino que de hecho funcionará con un tema de comportamiento (o más importante, algo que es * upstream y se sabe que es un BehaviorSubject **, o combineLatest
que toma dos valores de BehaviorSubject
). Y definitivamente no vayas a hacer (obs$ as BehaviorSubject)
- ugh!
Este ejemplo anterior todavía se considera una mala práctica en general, es un desastre. Solo hago el estilo de código anterior si quiero ver si un valor está disponible de inmediato y poder detectar si no lo está.
Mejor acercamiento
Usted está mucho mejor si puede mantener todo lo más observable el mayor tiempo posible, y solo suscribirse cuando realmente necesite el valor, y no intente "extraer" un valor en un ámbito de contenido que es lo que estoy haciendo. encima.
p.ej. Digamos que queremos hacer un informe de nuestros animales, si su zoológico está abierto. Podría pensar que quiere el valor ''extraído'' de zooOpen$
esta manera:
Mal camino
zooOpen$: Observable<boolean> = of(true); // is the zoo open today?
bear$: Observable<string> = of(''Beary'');
lion$: Observable<string> = of(''Liony'');
runZooReport() {
// we want to know if zoo is open!
// this uses the approach described above
const zooOpen: boolean = undefined;
const sub = this.zooOpen$.subscribe(open => zooOpen = open);
sub.unsubscribe();
// ''zooOpen'' is just a regular boolean now
if (zooOpen)
{
// now take the animals, combine them and subscribe to it
combineLatest(this.bear$, this.lion$).subscribe(([bear, lion]) => {
alert(''Welcome to the zoo! Today we have a bear called '' + bear + '' and a lion called '' + lion);
});
}
else
{
alert(''Sorry zoo is closed today!'');
}
}
Entonces, ¿por qué es esto tan malo?
- ¿Qué pasa si
zooOpen$
proviene de un servicio web? ¿Cómo funcionará el ejemplo anterior? En realidad, no importa lo rápido que sea su servidor: ¡nunca obtendría un valor con el código anterior sizooOpen$
fuera un http observable! - ¿Qué pasa si desea utilizar este informe ''fuera'' de esta función. Ahora ha bloqueado la
alert
en este método. ¡Si tiene que usar el informe en otro lugar, tendría que duplicar esto!
Buen camino
En lugar de intentar acceder al valor en su función, considere en su lugar una función que crea un nuevo Observable y que ni siquiera se suscribe a él.
En cambio, devuelve un nuevo observable que puede ser consumido ''afuera''.
Al mantener todo como observable y usar switchMap
para tomar decisiones, puede crear nuevos observables que pueden ser la fuente de otros observables.
getZooReport() {
return this.zooOpen$.pipe(switchMap(zooOpen => {
if (zooOpen) {
return combineLatest(this.bear$, this.lion$).pipe(map(([bear, lion] => {
// this is inside ''map'' so return a regular string
return "Welcome to the zoo! Today we have a bear called '' + bear + '' and a lion called '' + lion;
}
);
}
else {
// this is inside ''switchMap'' so *must* return an observable
return of(''Sorry the zoo is closed today!'');
}
});
}
Lo anterior crea un nuevo observable para que podamos ejecutarlo en otros lugares y canalizarlo más si lo deseamos.
const zooReport$ = this.getZooReport();
zooReport$.pipe(take(1)).subscribe(report => {
alert(''Todays report: '' + report);
});
// or take it and put it into a new pipe
const zooReportUpperCase$ = zooReport$.pipe(map(report => report.toUpperCase()));
Tenga en cuenta lo siguiente:
- No me suscribo hasta que sea absolutamente necesario, en este caso, está fuera de la función.
- El observable de ''conducción'' es
zooOpen$
y que usaswitchMap
para ''cambiar'' a un observable diferente que es, en última instancia, el que devuelvegetZooReport()
. - La forma en que esto funciona si
zooOpen$
cambia, entonces cancela todo y comienza nuevamente dentro del primerswitchMap
. Lea sobreswitchMap
para más sobre eso. - Nota: El código dentro de
switchMap
debe devolver un nuevo observable. Puedes hacer uno rápidamente conof(''hello'')
- o devolver otro observable comocombineLatest
. - Del mismo modo: el
map
solo debe devolver una cadena normal.
Tan pronto comencé a hacer una nota mental para no suscribirme hasta que tuve que hacerlo, de repente empecé a escribir un código mucho más productivo, flexible, más limpio y fácil de mantener.
Otra nota final: si utiliza este enfoque con Angular, podría tener el informe del zoológico anterior sin una sola subscribe
al usar | async
tubería | async
. Este es un gran ejemplo de "no suscribirse hasta que TENGA que" el director en la práctica.
// in your angular .ts file for a component
const zooReport$ = this.getZooReport();
y en su plantilla:
<pre> {{ zooReport$ | async }} </pre>
Véase también mi respuesta aquí:
https://.com/a/54209999/16940
Tampoco se menciona arriba para evitar confusiones:
-
tap()
puede ser útil a veces para "obtener el valor de un observable". Si no está familiarizado con ese operador lea en él. RxJS usa ''tuberías'' y un operador detap()
es una forma de ''conectarse a la tubería'' para ver qué hay allí.