When I create an observable and I am done with it I unsubscribe it directly
const data$ = this.httpClient.get('https://jsonplaceholder.typicode.com/todos/1').subscribe(res => {
console.log('live', res);
data$.unsubscribe(); // <---- works fine
});
But say if I create an Observable using of and try to do the same
const obs$ = of(1).subscribe(e => {
console.log('test', e)
obs$.unsubscribe(); // <--- Problem while creating Observable by of
});
Whats different between these 2 observables?
Your code should be import Subscription and unsubscribe in ngOnDestroy
import { Observable, Subscription, of } from "rxjs";
private subscription$: Subscription;
this.subscription$ = of(1).subscribe(e => {
console.log('test', e)
});
ngOnDestroy() {
this.subscription$.unsubscribe();
}
Update: What I understand is http request is an observable that potentialy have incoming value in the future and of simply create a list of observable
And from #cartant comment
of completes synchronously, so you are attempting to call
obs$.unsubscribe before the assignment to obs$ has been made.
If you have only one observable in your component that would be the easiest scenario for unsubscribing by the following way:
ngOnDestroy() {
this.subscription$.unsubscribe();
}
the more complex scenario is having multiple observables, at that moment you will not to push each observable while subscribing in subscriptions: Subscription[] and while destroying do unsubscribe all added subscriptions in the array ngOnDestroy(){
this.subscriptions.forEach(sub => sub.unsubscribe());
}
or you can use that npm package
For ensuring unsubscription in a component, I would recommend creating a Subject which emits in ngOnDestroy() like this:
destroy$ = new Subject<boolean>();
...
ngOnDestroy() {
this.destroy$.next(true);
}
And adding a takeUntil on each Observable in the component:
myObs$.pipe(
takeUntil(this.destroy$),
tap(stuff => doStuff())
).subscribe();
This way you avoid polluting things with loads of unnecessary Subscription variables.
Related
I want to wait until elements are rendered in the dom to dispatch an event. I have a lit element that is wrapped around a react element.
In the connectedCallback I have the following
connectedCallback() {
super.connectedCallback();
CommentsManager.register(this);
const event = new Event('ccx-comments-loaded');
window.dispatchEvent(event);
}
in the constructor, I have the following
this.isReadyPromise = new Promise(function(resolve, reject) {
window.addEventListener('ccx-comments-loaded', () => {
resolve(true);
});
});
How can I remove the listener that I created?
I want to wait until elements are rendered in the dom to dispatch an
event.
This looks like you could use an already existing updateComplete method from lit-element lifecycle. It is executed after render and it sounds like you may want to use it instead of having your own events.
You could read more about it here:
https://lit.dev/docs/v1/components/lifecycle/#updatecomplete
This would be a clean and more straightforward way to use lit-element. This way you don't reinvent something existing and your code would be more straightforward and clear for the other developers.
Store a reference to the Event Listener, then remove it in the disconnectedCallback
customElements.define("my-element", class extends HTMLElement {
constructor() {
super();
this.listener = window.addEventListener("click", () => {
this.remove();
});
}
connectedCallback() {
this.innerHTML = `Listening! Click to remove Web Component`;
}
disconnectedCallback() {
// element is no longer in the DOM; 'this' scope still available!!!
window.removeEventListener("click", this.listener);
document.body.append("Removed Web Component and Listener");
}
})
<my-element></my-element>
I am using NestJS HttpModule to make GET request for one end point. The code is somewhat like this:
#Injectable
export class AnimalService {
constructor(private httpService: HttpService){}
getAnimalData(variant: string): Observable<AxiosResponse<Animal>> {
return this.httpService
.get(`http://animal.test/${variant}`)
.pipe(map((response) => response.data));
}
}
And now I want to create a method which calls multiple endpoints simultaneously.
getAllAnimalsData() {
// const variants = ['birds', 'cats', 'dogs'];
// call
// http://animal.test/birds
// http://animal.test/cats
// http://animal.test/dogs
// simultaneously
// and process the response data
}
How can I achieve this using NestJS HttpModule?
How can I process every result?
How can I handle if there is a partial error (e.g. 1 of 3 request has an error)?
If you want to make multiple http requests simultaneously you can use the RxJS forkJoin operator. I also added a catchError to each Observable so that any errors will be passed to the subscribe callback. The subscribe callback triggers when all Observables are finished.
getAllAnimalsData() {
const variants = ['birds', 'cats', 'dogs'];
forkJoin(
variants.map(v =>
getAnimalData(v).pipe(catchError(e => of(e)))
)
)
.subscribe(([r1, r2, r3]) => /* ... */)
}
I am looking for an option to use nest as a back-end Gateway service -
The idea is to poll on DB changes ( and maybe later to move it to event driven ) - de facto no listener would be required here.
On change the Nest would update a 3rd pt API calls
What would be best practice here ?
Take a look here, I'm doing something similar to what you're after: https://github.com/nerdybeast/sith-api/blob/feature/redis-cache/src/modules/api/sobjects/trace-flag/TraceFlagPoller.ts
I created a class that "polls" a backend and emits an event when it detects a change in that backend. You could have other code that listens for this event which makes the call to your 3rd party api.
UPDATE:
As you stated, Nest does have a basic application context which skips the http service setup, here's how you can do that:
index.ts
import { NestFactory } from '#nestjs/core';
import { ApplicationModule } from './ApplicationModule';
import { DatabaseService } from './DatabaseService';
(async () => {
const app = await NestFactory.createApplicationContext(ApplicationModule);
const databaseService = app.get<DatabaseService>(DatabaseService);
await databaseService.poll();
})();
DatabaseService.ts
#Injectable()
export class DatabaseService {
private expectedResult: any;
public async poll() : Promise<void> {
const result = await getData();
if(result !== this.expectedResult) {
this.expectedResult = result;
await axios.post('https://some-url.com', result);
}
//Poll every 5 seconds or whatever
setTimeout(() => this.poll(), 5000);
}
}
This could be the solution if you had to poll the database instead of being able to subscribe to it. With this approach, when you start the app, it will poll forever, constantly updating your 3rd party api.
I would start the index.ts file with pm2 or forever so that you can have a graceful restart if your process crashes for some reason.
I would personally use typeORM subscribers like I have done many times for similar requirements. However I use an eventEmitter to not block the saving action. This is a snippet of what I usually do.
#Injectable()
export class EntityUpdate implements EntitySubscriberInterface {
constructor(
#InjectConnection() readonly connection: Connection,
#InjectEventManager() emitter: AppEvents<AbstractEntity>,
) {
connection.subscribers.push(this);
}
afterInsert(event: InsertEvent<AbstractEntity>): void {
this.emitter('entity', {
method: 'update',
entity,
});
}
}
Then I could listen to the event anywhere within my application and handle that status change of the entity
I have a situation. I use Node.js to connect to a special hardware. let assume that I have two functions to access the hardware.
hardware.send('command');
hardware.on('responce', callback);
At first, I made a class to interface this to the application layer like this (I write simplified code over here for better understanding)
class AccessHardware {
constructor() {
}
updateData(callback) {
hardware.on('responce', callback);
hardware.send('command');
}
}
Now, the problem is that if there are multiple requests from the application layer to this access layer, they should not send multiple 'command' to the hardware. Instead, they should send one command and all of those callbacks can be served once the hardware answer the command.
So I update the code something like this:
class AccessHardware {
constructor() {
this.callbackList = [];
hardware.on('responce', (value) => {
while (this.callbackList.length > 0) {
this.callbackList.pop()(value);
}
});
}
updateData(callback) {
if (this.callbackList.length == 0) {
hardware.send('command');
}
this.callbackList.push(callback);
}
}
Of course, I prefer to use promise to handle the situation. so what is your suggestion to write this code with promise?
Next question, is this approach to make a 'list of callbacks' good?
I prefer to use promise to handle the situation. So what is your suggestion to write this code with promise?
You'd store a promise in your instance that will be shared between all method callers that want to share the same result:
class AccessHardware {
constructor(hardware) {
this.hardware = hardware;
this.responsePromise = null;
}
updateData() {
if (!this.responsePromise) {
this.responsePromise = new Promise(resolve => {
this.hardware.on('responce', resolve);
this.hardware.send('command');
});
this.responsePromise.finally(() => {
this.responsePromise = null; // clear cache as soon as command is done
});
}
return this.responsePromise;
}
}
Btw, if hardware is a global variable, there's no reason to use a class here.
Is the current solution to make a 'list of callbacks' good?
Yes, that's fine as well for a non-promise approach.
I've just started angular 2. I've done an angular2 sample as given in the https://angular.io/guide/quickstart
when I run the project in Firefox using
npm start
command in terminal, the connection get disconnected after output showing once.Error showing like
The connection to ws://localhost:3000/browser-sync/socket.io/?EIO=3&transport=websocket&sid=6YFGHWy7oD7T7qioAAAA was interrupted while the page was loading
Any idea about how to fix this issue ?
I don't know how you manage your web socket but you could consider using the following code. This idea is to wrap the web socket into an observable.
For this you could use a service like below. The initializeWebSocket will create a shared observable (hot) to wrap a WebSocket object.
export class WebSocketService {
initializeWebSocket(url) {
this.wsObservable = Observable.create((observer) => {
this.ws = new WebSocket(url);
this.ws.onopen = (e) => {
(...)
};
this.ws.onclose = (e) => {
if (e.wasClean) {
observer.complete();
} else {
observer.error(e);
}
};
this.ws.onerror = (e) => {
observer.error(e);
}
this.ws.onmessage = (e) => {
observer.next(JSON.parse(e.data));
}
return () => {
this.ws.close();
};
}).share();
}
}
You could add a sendData to send data on the web socket:
export class WebSocketService {
(...)
sendData(message) {
this.ws.send(JSON.stringify(message));
}
}
The last point is to make things a bit robust, i.e. filter received messages based on a criteria and implement retry when there is a disconnection. For this, you need to wrap our initial websocket observable into another one. This way we can support retries when the connection is lost and integrate filtering on criteria like the client identifier (in the sample the received data is JSON and contains a sender attribute).
export class WebSocketService {
(...)
createClientObservable(clientId) {
return Observable.create((observer) => {
let subscription = this.wsObservable
.filter((data) => data.sender!==clientId)
.subscribe(observer);
return () => {
subscription.unsubscribe();
};
}).retryWhen((errors) => {
return Observable.timer(3000);
});
}
}
You can see that deconnections are handled in this code using the retryWhen operator of observable.