I am having trouble trying to run functions sequentially in my Angular 2/4 project.
I have retrieveData()
function that retrieves data from data service and assigns them into an array that I declared.
Then I have displayData()
function that uses the data stored in the array and display them in a chart.
When I try to run them, for eg:
function(){
this.retrieveData();
this.displayData();
}
displayData()
function runs first before retrieveData()
mainly because of the data service in the retrieveData()
function. Therefore the graph cannot be displayed properly.
One of the ways that I found that I can run functions sequentially is async.waterfall
from async
library but I am having trouble importing the library into my project, with the console log saying:
Uncaught Error: Unexpected value 'waterfall' imported by the module 'AppModule'. Please add a @NgModule annotation.
I don't want to use Promises and Observables because they require the initial function to have some kind of return value to pass on to the next functions. I somewhat managed to achieve it using setTimeOut()
function but I really doubt the reliability and robustness of the approach.
So, any help with the use of async
in Angular 2/4, or any kind of ways to make the functions wait without any kind of return promises?
UPDATE
Sorry for the confusion and inconvenience caused guys. I was posting and asking for an over-simplified version. Here is the more complete part of my code. I am an Angular and Typescript noob, and more noob when it comes to Async programming techniques.
Below is my implementation of promises in retrieveAllData()
method. It doesn't give any error in either compile time or runtime. But when the functions are still running asynchronously, i.e. refreshAllCharts()
still runs before retrieveAllData()
. Is there any flaws in my implementation of promises?
import { Component, OnInit, AfterContentInit } from '@angular/core';
import { DataService } from '../data.service';
import {BaseChartDirective} from 'ng2-charts/ng2-charts';
import {IMyDpOptions,IMyDateModel} from 'mydatepicker';
//import {MomentTimezoneModule} from 'angular-moment-timezone';
import * as moment from 'moment-timezone';
// import async from 'async-waterfall';
@Component({
templateUrl: 'chartjs.component.html'
})
export class ChartJSComponent {
tempArr = []; //array to store temperature values for the chart
timeTempArr = []; //array to store timestamps for the chart label
device = "1CB001"; //a parameter used for the data service method to query the database
dateSelected; //variable to store the date chosen from the datepicker on the html side of the component
constructor(private dataService: DataService){
}
ngOnInit(){
}
//function to retrieve temperature values and assign them into "tempArr" array
retrieveTempDataAssign(){
var _arr = new Array();
this.dataService.getData(this.device, this.dateSelected).subscribe(response => {
console.log("Response: " + JSON.stringify(response));
for(var item of response){
_arr.push(item.Temperature);
}
this.tempArr = _arr;
console.log("Array assigned Temp: " + this.tempArr);
});
this.retrieveTempTimeDataAssign();
}
//function to retrieve time values and assign the date and time objects into "timeTempArr" array
retrieveTempTimeDataAssign(){
var _arr = new Array();
this.dataService.getData(this.device, this.dateSelected).subscribe(response => {
for(var item of response){
// var value = "'" + item.Date + "'";
// _arr.push(value);
var value = item.Date;
var time = moment.tz(value, "Asia/singapore");
_arr.push(time);
}
this.timeTempArr = _arr;
console.log("Array assigned Time: " + this.timeTempArr);
});
}
//function to refresh the whole of Temperature chart
refreshTempChart(){
this.showTempData();
setTimeout(() => this.showTempLabels(), 500);
}
//function to assign the "tempArr" array into the dataset for the temperature chart
showTempData(){
console.log("To display: " + this.tempArr);
this.datasetsTemp = [{
label: "Values",
data: this.tempArr
}];
}
//function to assign the "timeTempArr" array into the labels for the temperature chart
showTempLabels(){
console.log("To label: " + this.timeTempArr);
this.labels = this.timeTempArr;
}
//date picker format
private myDatePickerOptions: IMyDpOptions = {
dateFormat: 'yyyy-mm-dd',
};
//change event listener on the datepicker
onDateChanged(event: IMyDateModel){
this.dateSelected= event.formatted;
console.log("Selected Date: " + this.dateSelected);
//**The implementation part**
this.retrieveAllData().then(()=>{
this.refreshAllCharts();
})
}
//to run all functions to retrieve respective data
retrieveAllData(){
return new Promise((resolve, reject) => {
this.retrieveTempDataAssign(); //assign the retrieved values into the array first
return true;
});
}
//to run all functions to update all the charts
refreshAllCharts(){
this.refreshTempChart();
}
//objects used by the chart to display data
private datasetsTemp = [
{
label: "Values",
data: []
}
];
private labels = [];
private options = {
scales: {
xAxes: [{
display: true,
type: "time",
time: {
unit: "hour",
tooltipFormat: 'YYYY-MM-DD hh:mm A'
},
scaleLabel: {
display: true,
labelString: 'Time'
}
},],
yAxes: [{
ticks: {
beginAtZero: false
}
}]
}
};
}
You don't have to return any value to pass on to the next functions while using promise.
If you want to keep you function signature untouched (i.e. retrieveData()
and displayData()
do not take any parameters and return void) consider using promises like this:
private dataStorage: string = null;
private retrieveDataResolver;
displayData(): void {
// your display code goes here
console.log("2. DISPLAYING DATA", this.dataStorage);
}
retrieveData(): void {
// your async retrieval data logic goes here
console.log("1. GETTING DATA FROM SERVER");
setTimeout(() => { // <--- Change it - your service data retrieval
this.dataStorage = '++DATA++';
this.retrieveDataResolver(); // <--- This must be called as soon as the data are ready to be displayed
}, 1000);
}
retrieveDataPromise(): Promise<any> {
return new Promise((resolve) => {
this.retrieveDataResolver = resolve;
this.retrieveData();
})
}
retrieveAndThenDisplay() {
this.retrieveDataPromise().then(() => {this.displayData()});
}
I recommend using promise wrapper as powerful chaining construct to serialize asynchronous operations
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