¿Es necesario darse de baja de los observables creados por métodos Http?
¿Necesita darse de baja de las llamadas http de Angular 2 para evitar pérdidas de memoria?
fetchFilm(index) {
var sub = this._http.get(`http://example.com`)
.map(result => result.json())
.map(json => {
dispatch(this.receiveFilm(json));
})
.subscribe(e=>sub.unsubscribe());
...
Entonces la respuesta es no, no lo haces. Ng2
lo limpiará solo.
La fuente del servicio Http, de la fuente backend Http XHR de Angular:
Observe cómo se ejecuta complete()
después de obtener el resultado. Esto significa que en realidad se da de baja al finalizar. Por lo tanto, no es necesario que lo haga usted mismo.
Aquí hay una prueba para validar:
fetchFilms() {
return (dispatch) => {
dispatch(this.requestFilms());
let observer = this._http.get(`${BASE_URL}`)
.map(result => result.json())
.map(json => {
dispatch(this.receiveFilms(json.results));
dispatch(this.receiveNumberOfFilms(json.count));
console.log("2 isUnsubscribed",observer.isUnsubscribed);
window.setTimeout(() => {
console.log("3 isUnsubscribed",observer.isUnsubscribed);
},10);
})
.subscribe();
console.log("1 isUnsubscribed",observer.isUnsubscribed);
};
}
Como era de esperar, puede ver que la suscripción siempre se cancela automáticamente después de obtener el resultado y terminar con los operadores observables. Esto sucede en un tiempo de espera (n.º 3) para que podamos verificar el estado del observable cuando esté todo hecho y completado.
y el resultado
Por lo tanto, ¡no existiría ninguna fuga debido a Ng2
las cancelaciones automáticas de suscripción!
Es bueno mencionarlo: esto Observable
se clasifica como finite
, por el contrario, infinite
Observable
se puede emitir un flujo infinito de datos como click
el oyente DOM, por ejemplo.
GRACIAS, @rubyboy por ayuda con esto.
¡¡¡De qué están hablando!!!
Bien, entonces hay dos razones para cancelar la suscripción a cualquier observable. ¡Nadie parece estar hablando mucho sobre la muy importante segunda razón!
- Limpiar recursos. Como otros han dicho, este es un problema insignificante para los observables HTTP. Simplemente se limpiará solo.
subscribe
Evite que se ejecute el controlador.
Tenga en cuenta que con un observable HTTP, cuando cancele su suscripción, cancelará la solicitud subyacente en su navegador (verá "Cancelado" en rojo en el panel Red). Esto significa que no se perderá tiempo descargando o analizando la respuesta. Pero eso es en realidad un aparte de mi punto principal a continuación.
La relevancia del número 2 dependerá de lo que haga su controlador de suscripción:
Si su
subscribe()
función de controlador tiene algún tipo de efecto secundario no deseado si cualquier llamada se cierra o se elimina, entonces debe cancelar la suscripción (o agregar lógica condicional) para evitar que se ejecute.
Consideremos algunos casos:
Un formulario de inicio de sesión. Ingresa nombre de usuario y contraseña y hace clic en 'Iniciar sesión'. ¿Qué pasa si el servidor es lento y decides presionar Escape para cerrar el cuadro de diálogo? Probablemente asumirá que no inició sesión, pero si la solicitud http regresó después de presionar Escape, aún ejecutará cualquier lógica que tenga allí. Esto puede resultar en una redirección a la página de una cuenta, en la configuración de una cookie de inicio de sesión no deseada o de una variable de token. Probablemente esto no sea lo que esperaba su usuario.
Un formulario de 'enviar correo electrónico'.
Si el subscribe
controlador de 'sendEmail' hace algo como activar una animación 'Su correo electrónico ha sido enviado', lo transfiere a una página diferente o intenta acceder a cualquier cosa que se haya eliminado, puede obtener excepciones o comportamientos no deseados.
También tenga cuidado de no asumir unsubscribe()
que significa "cancelar". Una vez que el mensaje HTTP esté en tránsito unsubscribe()
NO cancelará la solicitud HTTP si ya llegó a su servidor. Solo cancelará la respuesta que le llegue. Y el correo electrónico probablemente se enviará.
Si crea la suscripción para enviar el correo electrónico directamente dentro de un componente de la interfaz de usuario, probablemente desee cancelar la suscripción al eliminarlo, pero si el correo electrónico lo envía un servicio centralizado que no es de la interfaz de usuario, probablemente no sea necesario.
- Un componente angular que está destruido/cerrado. Cualquier observable http que aún se esté ejecutando en ese momento se completará y ejecutará su lógica a menos que cancele la suscripción en
onDestroy()
. Si las consecuencias son triviales o no dependerá de lo que haga en el controlador de suscripción. Si intenta actualizar algo que ya no existe, es posible que obtenga un error.
A veces es posible que desee realizar algunas acciones si se desecha el componente y otras no. Por ejemplo, tal vez tengas un sonido de "swoosh" para un correo electrónico enviado. Probablemente querrás que esto se reproduzca incluso si el componente está cerrado, pero si intentas ejecutar una animación en el componente, fallará. En ese caso, la solución sería alguna lógica condicional adicional dentro de la suscripción, y NO querrá cancelar la suscripción del observable http.
Entonces, en respuesta a la pregunta real, no, no es necesario hacerlo para evitar pérdidas de memoria. Pero debe hacerlo (a menudo) para evitar que se desencadenen efectos secundarios no deseados al ejecutar código que pueda generar excepciones o dañar el estado de su aplicación.
Consejo: Subscription
Contiene una closed
propiedad booleana que puede resultar útil en casos avanzados. Para HTTP, esto se establecerá cuando se complete. En Angular, en algunas situaciones puede resultar útil establecer una _isDestroyed
propiedad ngDestroy
que su controlador pueda verificar subscribe
.
Consejo 2: Si maneja varias suscripciones, puede crear un new Subscription()
objeto ad-hoc y add(...)
cualquier otra suscripción a él, de modo que cuando cancele la suscripción a la principal, también cancelará todas las suscripciones agregadas.
Cancelar la suscripción es IMPRESCINDIBLE si desea un comportamiento determinista en todas las velocidades de la red.
Imagine que el componente A se representa en una pestaña: hace clic en un botón para enviar una solicitud 'OBTENER'. Se necesitan 200 ms para que llegue la respuesta. Por lo tanto, puede cerrar la pestaña con seguridad en cualquier momento sabiendo que la máquina será más rápida que usted y que la respuesta http se procesará y completará antes de que se cierre la pestaña y se destruya el componente A.
¿Qué tal en una red muy lenta? Haces clic en un botón, la solicitud 'OBTENER' tarda 10 segundos en recibir su respuesta, pero después de 5 segundos de espera decides cerrar la pestaña. Eso destruirá el componente A que será recolectado como basura más adelante. ¡Espera un minuto! , no nos cancelamos la suscripción; ahora , 5 segundos después, llega una respuesta y se ejecutará la lógica en el componente destruido. Esa ejecución ahora se considera out-of-context
y puede resultar en muchas cosas, incluido un rendimiento muy bajo y corrupción de datos/estado.
Por lo tanto, la mejor práctica es utilizar takeUntil()
y cancelar la suscripción a llamadas http cuando se destruye el componente.
Nota:
- RxJS no es específico de Angular
- La tubería angular
async
utilizada en las plantillas se da de baja al destruirse automáticamente - Darse de baja más de una vez no tiene ningún efecto secundario negativo, excepto las
no-op
llamadas adicionales
import { Component, OnInit, OnDestroy } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
interface User {
id: string;
name: string;
age: number;
}
@Component({
selector: 'app-foobar',
templateUrl: './foobar.component.html',
styleUrls: ['./foobar.component.scss'],
})
export class FoobarComponent implements OnInit, OnDestroy {
private user: User = null;
private destroy$ = new Subject();
constructor(private http: HttpClient) {}
ngOnInit() {
this.http
.get<User>('api/user/id')
.pipe(takeUntil(this.destroy$))
.subscribe(user => {
this.user = user;
});
}
ngOnDestroy(): void {
this.destroy$.next(); // trigger the unsubscribe
this.destroy$.complete(); // finalize & clean up the subject stream
}
}