Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to run functions sequentially in Angular 2/4 Typescript

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
        }
      }]
    }
  };
}
like image 218
Wei Minn Avatar asked Aug 06 '17 15:08

Wei Minn


1 Answers

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

like image 82
kuceraf Avatar answered Nov 15 '22 05:11

kuceraf