I have a service used as a local data store to share data across the app. This service makes a few rest calls in the beginning to initialize some data. It look like this:
Injectable()
export class DataStoreService {
leadList: LeadModel[]
optyList: ContactModel[]
constructor(public dataService:DataService){
this.initializeData()
}
initializeData() {
this.dataService.getObjectData('opportunities').subscribe(
(data) => this.optyList = data
)
this.dataService.getObjectData('leads').subscribe(
(data) => this.leadList = data
)
}
}
I have a component page where I do below:
ngOnInit() {
for(let lead of this.dataStore.leadList){
if(lead.accepted == false)
this.newLeadsList.push(lead)
}
}
It is very obvious that if initialize data fails to finish the leadList may be undefined and this ngOnInit for loop will crash as well. So, how do I wait in the ngOnInit until initializeData finishes?
Use . toPromise on your observable followed by async/await .
Promises can be executed by calling the then() and catch() methods. The then() method takes two callback functions as parameters and is invoked when a promise is either resolved or rejected. The catch() method takes one callback function and is invoked when an error occurs.
However, if you're using ngOnInit instead of the constructor to wait for a function to complete, you're basically doing the equivalent of this: It will run the async function, but it WILL NOT wait for it to complete. If you notice sometimes it completes and sometimes it doesn't, it really just depends on the timing of your function.
It will run the async function, but it WILL NOT wait for it to complete. If you notice sometimes it completes and sometimes it doesn't, it really just depends on the timing of your function. Using the ideas from this post, you can basically run outside zone.js. NgZone does not include scheduleMacroTask, but zone.js is imported already into angular.
Sometimes in Angular, you gotta wait for multiple HTTP requests to complete before displaying a page. That's when you should turn to forkJoin. The forkJoin operator gives you the last emitted value from any number of Observable s. However, it only does that once they all complete.
Use of async or await () function: This method can be used if the exact time required in setTimeout () cannot be specified. The async keyword is used to create an asynchronous function that returns a promise that is either rejected or resolved.
remove initializeData()
from constructor and do something like this:
public async initializeData()
{
this.optyList = await this.dataService.getObjectData('opportunities').toPromise();
this.leadList = await this.dataService.getObjectData('leads').toPromise();
}
async ngOnInit()
{
await initializeData();
for(let lead of this.dataStore.leadList){
if(lead.accepted == false)
this.newLeadsList.push(lead)
}
}
I made code from head so it can have some bugs - may be async should be also before function ngOnInit()
... check this.
Async-await-toPromise make that your asynchronous function behaves as synchronous function... and JS wait until your toPromise finish before execute another promise...
If I understand you right: you wanna call your service "DataStoreService.initializeData()
" once and use it in future in other components (without calling initializeData again) - ? - if yes then you need Service singleton (which can also use inside above async-await technique)
What you can utilize is observables, in this case a BehaviorSubject
which you subscribe to and always emits if there is a subscriber. After making the initial request, just call next
and all components that are subscribing will get the value... when it eventually arrives:
DataStoreService:
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
// ...
leadList: LeadModel[]
leadList$ = new BehaviorSubject<LeadModel[]>([])
constructor(public dataService:DataService){
this.initializeData()
}
initializeData() {
this.dataService.getObjectData('leads')
.subscribe((data) => {
this.leadList = data;
this.leadList$.next(this.leadList)
})
}
Then in your components, subscribe to leadList$
:
leadList = <LeadModel[]>[];
ngOnInit() {
this.dataStore.leadList$.subscribe(data => {
this.leadList = data;
// do your magic here :)
})
}
Then remember to unsubscribe when component is destroyed!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With