Reputation: 1121
I have an Angular wizard component containing 2 steps. In the first step a form is shown to the user. In the second step, another form is shown as the result of a long-running HTTP GET. Currently this works, when the user arrives at the second step, the request is made and eventually the user sees the form as the result of the long-running call; however it is annoying for the user to wait for the request to end.
In order to optimize I'm trying to fire the long-running request in the wizard first step in the background such that the waiting time for the user is reduced. Technically this is possible because the request parameters do not depend on the user actions in in step 1.
However, the difficulty is that Observables do nothing unless you subscribe to them. But when I subscribe to the Observable request, it will wait in wizard step 1 for it to complete (thus no performance gain for the user).
So I need to subscribe to the observable request somehow, but in the background such that is does not block the begin pipeline.
Each wizard step has an onStepBegin
and onStepFinish
. In the onStepBegin
of step 1, I want to immediately create the observable and execute the request. Then in the onStepBegin
of step 2, it should wait for the remaining time of the request.
Technically the code below works, but the request is fired off no earlier than in step 2. How can I make it immediately fire off the request in step 1?
I tried the following things to fix it:
subscribe()
call; but so-called nested subscriptions seem to create memory leaksWizard step 1:
public onStepBegin(begin: Observable<any>): Observable<MyContext> {
return begin.pipe(
tap(() => {
..
this.context.loadInitial = this.loadInitialConfigurations(this.selectedElementclass.id).pipe(
first(),
publish()
);
})
);
}
Wizard step 2:
public onStepBegin(begin: Observable<any>): Observable<MyContext> {
return begin.pipe(
switchMap(() => this.context.loadInitial),
tap((c) => {
this.context.configurations = c;
}),
);
Upvotes: 0
Views: 2031
Reputation: 13515
Building on Jon's comment to your question, you need to kick off the HTTP request for step 2 data at the same time as you kick off step 1.
When the HTTP response comes back for step 2 data, you can update a subject that you have created.
When it comes time to move to step 2, you now subscribe to the subject.
If the HTTP response has already come back, then you already have your data. If the HTTP request is still being processed, step 2 will start once the response comes back.
private step2data$: Subject<any> = new ReplaySubject<any>(1);
ngOnInit(): void {
// kick off step one - however you do that
this.beginStepOne().subscribe();
// get step 2 data - however you do that
this.http.get('http://mydatasource.com').subscribe(data => {
this.step2data$.next(data);
});
}
ngOnDestroy(): void {
// tidy up
this.step2data$.complete();
}
private beginStepOne(): Observable<MyContext> {
// TODO: implement
}
// called when moving from step 1 to 2 - however you do that
private beginStepTwo(): Observable<MyContext> {
// get data immediately if present, or wait for ongoing request to come back
return this.step2data$.pipe(
// we only need to take the first - like a normal http request
take(1),
// now switch to whatever method you use to kick off step 2
switchMap(data => /* TODO: implement */)
);
}
Upvotes: 2