英文:
RXJS observers slow down each other
问题
I have the page with the documents list on the left (with clickable items, each item will select a document), and the actual content of the selected document in the right side, which takes up the bigger part of the page. The problem is that when a big document is clicked, it causes the delay to display it, but more importantly, the selected menu item (from EvahubSidenavComponent
) is not displayed immediately, but it seems to be waiting for the document content (in EvahubDocumentsComponent
) to be displayed first. Why? The document itself can be as much as 10MBs of text, so it's okay if it needs to be read-in over a second or two, but the menu item on the left (in the evahub-sidenav) should be colored immediately. I have these two crucial observations in the state store as so:
currentDocumentId$: Observable
state => state.currentUser.currentDocumentId
);
// For simplification, I set so the clicked document is always going from 0 to 3, so switcher is serving that purpose as index
switcher = 0;
selectedDocument$: Observable
combineLatestWith(this.currentPageIndex$, this.userLogs$),
map(([docId, cpi, ls]) => {
return ls[this.switcher++ % 3];
})
);
App component observes the currently selected document's id, so in app.component.ts I have this:
currentDocumentId$ = this.store.currentDocumentId$;
In the template (app.component.html) I have a child component (EvahubSidenavComponent) that the currentDocumentId$ is propagated to simply as input, so like this:
Router will in turn contain the 2nd component (EvahubDocumentsComponent), which uses selectedDocument$ from the store like this in its template EvahubDocumentsComponent.html
{{ asyncData.selectedDocument.getDocumentContent() }}
and takes it like this, in the component itself:
selectedDocument$ = this.store.selectedDocument$;
To recap, the derived observable selectedDocument$ is somehow stopping its factor observable, currentDocumentId$, until the derived observable finishes and returns. This is not good; I want them to be processed separately and independently. I tried this for the derived observable, with the same outcome, i.e. the currentDocumentId$ is delayed:
selectedDocument$: Observable
mergeMap(cdi =>
this.currentPageIndex$.pipe(
combineLatestWith(this.userLogs$),
map(uls => {
return uls[1].find(l => l.logId == cdi);
})
)
)
);
英文:
I have the page with the documents list on the left (with clickable items, each items will select a document), and the actual content of the selected document in the right side, which takes up the bigger part of the page. The problem is that when a big document is clicked, it causes the delay to display it, but more importantly, the selected menu item (from EvahubSidenavComponent
) is not displayed immediately, but it seems to be waiting the document content (in EvahubDocumentsComponent
) to be displayed first. Why? The document itself, can be as much as 10MBs of text, so it's ok if it needs to be read-in over a second or two, but the menu item on the left (in the evahub-sidenav) should be coloured immediately. I have these two crucial observes in the state store as so:
currentDocumentId$: Observable<number> = this.select(
state => state.currentUser.currentDocumentId
);
// for simplification, I set so the clicked document is always going from 0 to 3, so switcher is serving that purpose as index
switcher = 0;
selectedDocument$: Observable<EvahubDocument> = this.currentDocumentId$.pipe(
combineLatestWith(this.currentPageIndex$, this.userLogs$),
map(([docId, cpi, ls]) => {
return ls[this.switcher++ % 3];
})
);
App component observes the currently selected document's id, so in app.component.ts I have this:
currentDocumentId$ = this.store.currentDocumentId$;
In the template (app.component.html
) I have a child component (EvahubSidenavComponent
) that the currentDocumentId$
is propagated to simply as input, so like this:
<div *ngIf="{
currentDocumentId: currentDocumentId$ | async,
...
} as asyncData"
>
<evahub-sidenav
...
[selectedDocumentId]="asyncData.currentDocumentId"
...
></evahub-sidenav>
</div>
<router-outlet></router-outlet>
Router will in turn contain the 2nd component (EvahubDocumentsComponent
), which uses selectedDocument$
from the store like this in its template EvahubDocumentsComponent.html
<div
*ngIf="{
selectedDocument: selectedDocument$ | async,
currentPageIndex: currentPageIndex$ | async
} as asyncData"
>
{{ asyncData.selectedDocument.getDocumentContent() }}
and takes it like this, in the component itself:
selectedDocument$ = this.store.selectedDocument$;
To recap, the derived observable selectedDocument$
is somehow stopping its factor observable, currentDocumentId$
, until the derived observable finishes and returns. This is not good, I want them to be processed separately and independently. I tried this for the derived observable, with same outcome, i.e. the currentDocumentId$ is delayed:
selectedDocument$: Observable<any> = this.currentDocumentId$.pipe(
mergeMap(cdi =>
this.currentPageIndex$.pipe(
combineLatestWith(this.userLogs$),
map(uls => {
return uls[1].find(l => l.logId == cdi);
})
)
)
);
答案1
得分: 1
My guess is that you are getting information via python as a (from JavaScript's perspective) synchronous operation.
My second guess is that, because selectedDocument$
isn't using the share()
operator, each observer is incrementing this.switcher
and that is (or might soon be) messing things up.
Both can be resolved by changing trying
selectedDocument$: Observable<EvahubDocument> = this.currentDocumentId$.pipe(
combineLatestWith(this.currentPageIndex$, this.userLogs$),
map(([docId, cpi, ls]) => {
return ls[this.switcher++ % 3];
}),
shareReplay(1),
observeOn(asyncScheduler)
);
shareReplay
ensures that late observers start off with the most-recent value that was emitted. Also, without it, the observable and the operators execute per observer.
observeOn
emits the value on an asynchronous scheduler.
英文:
My guess is that you are getting information via python as a (from JavaScript's perspective) synchronous operation.
My second guess is that, because selectedDocument$
isn't using the share()
operator, each observer is incrementing this.switcher
and that is (or might soon be) messing things up.
Both can be resolved by changing trying
selectedDocument$: Observable<EvahubDocument> = this.currentDocumentId$.pipe(
combineLatestWith(this.currentPageIndex$, this.userLogs$),
map(([docId, cpi, ls]) => {
return ls[this.switcher++ % 3];
}),
shareReplay(1),
observeOn(asyncScheduler)
);
shareReplay
ensures that late observers start off with the most-recent value that was emitted. Also, without it, the observable and the operators execute per observer.
observeOn
emits the value on an asynchronous scheduler.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
- angular
- javascript
- rxjs
- rxjs-observables
评论