web-dev-qa-db-fra.com

Comment exécuter les fonctions séquentiellement dans Angular 2/4 typescript

Je ne parviens pas à exécuter les fonctions de manière séquentielle dans mon projet Angular 2/4.

J'ai la fonction retrieveData() qui extrait les données du service de données et les assigne dans un tableau que j'ai déclaré.

Ensuite, j'ai la fonction displayData() qui utilise les données stockées dans le tableau et les affiche dans un graphique.

Quand j'essaie de les exécuter, par exemple:

function(){
  this.retrieveData();
  this.displayData();
}

La fonction displayData() s'exécute avant retrieveData() principalement à cause du service de données de la fonction retrieveData(). Par conséquent, le graphique ne peut pas être affiché correctement.

L'un des moyens par lesquels j'ai découvert que je pouvais exécuter des fonctions de manière séquentielle est async.waterfall de la bibliothèque async, mais je ne parviens pas à importer la bibliothèque dans mon projet. Le journal de la console affiche: Uncaught Error: Unexpected value 'waterfall' imported by the module 'AppModule'. Please add a @NgModule annotation.

Je ne veux pas utiliser Promises et Observables car ils nécessitent que la fonction initiale ait une sorte de valeur de retour à transmettre aux fonctions suivantes. J'ai un peu réussi à y parvenir en utilisant la fonction setTimeOut() mais je doute vraiment de la fiabilité et de la robustesse de l'approche.

Donc, toute aide avec l'utilisation de async dans Angular 2/4, ou tout autre moyen de faire attendre les fonctions sans aucune sorte de promesse de retour?

METTRE À JOUR

Désolé pour la confusion et les inconvénients causés les gars. Je postais et demandais une version trop simplifiée. Voici la partie la plus complète de mon code. Je suis un Angular et un Noob TypeScript, et encore plus quand il s'agit de techniques de programmation async.

Ci-dessous, ma mise en œuvre des promesses dans la méthode retrieveAllData(). Cela ne donne aucune erreur ni lors de la compilation, ni lors de l'exécution. Mais lorsque les fonctions sont encore exécutées de manière asynchrone, c'est-à-dire que refreshAllCharts() est toujours exécuté avant retrieveAllData(). Y a-t-il des défauts dans la mise en œuvre de mes promesses?

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
        }
      }]
    }
  };
}
4
Kelvin Wei Minn

Vous n'avez pas à renvoyer de valeur à transmettre aux fonctions suivantes lorsque vous utilisez promesse. Si vous souhaitez que votre signature de fonction ne soit pas modifiée (par exemple, retrieveData() et displayData(), ne prenez aucun paramètre et ne renvoyez rien), envisagez d'utiliser des promesses comme celle-ci:

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()});
  }

Je recommande l'utilisation de wrapper de promesse comme structure de chaînage puissante pour sérialiser les opérations asynchrones

3
kuceraf

À côté de votre mise à jour, vous pouvez enchaîner la promesse. obtenir d’abord des données et actualiser le graphe appelé lorsque les données sont prêtes. Ce lien peut vous aider à mieux comprendre les promesses: https://codecraft.tv/courses/angular/es6-TypeScript/promises/

// get data
var job1 = new Promise(function(resolve, reject){
    resolve('done1');
});


job1.then(function(data) {
    refreshGraphe();
})
2
sancelot

Essayez quelque chose comme ça:

  retrieveData(): Promise<any> {
      return this.dataService.getData(this.device, this.dateSelected)
          .map(response => response.json())
          .toPromise();
  }


  executeSerialFunctions() {
      this.retrieveData()
          .then(response => {
              for(var item of response){
                var value = item.Date;
                var time = moment.tz(value, "Asia/singapore");
                _arr.Push(time);
              }
              this.timeTempArr = _arr;
              console.log("Array assigned Time: " + this.timeTempArr);
              return response 
          }).then(response => {
              displayData();
          })
  }

La fonction retrieveData récupère les données via le service de données et renvoie une promesse.

Vous appelez executeSerialFunctions et il appellera retrieveData. Lorsque l'extraction sera terminée et que les données seront renvoyées, il sera traité et traité comme dans la fonction retrieveData de votre code (avec l'itérateur de réponse).

Une fois cette opération terminée, la réponse est renvoyée et .then est exécuté. Il mange effectivement la réponse, mais appelle displayData après le traitement de toutes les données.

Je ne peux pas tester cela, mais je pense que cela devrait fonctionner.

1
Stephen R. Smith

Il existe un excellent site qui explique les concepts asynchrones en termes rxjs , je l’ai trouvé très pratique car les documents rxjs sont écrits dans une terminologie très complexe.

Ils suggèrent que cela soit équivalent à la fonction async de la cascade:

var Rx = require('rx');

var async = {
    waterfall: series => {
        return Rx.Observable.defer(() => {
            var acc = series[0]();
            for (var i = 1, len = series.length; i < len; i++) {

                // Pass in func to deal with closure capture
                (function (func) {

                    // Call flatMapLatest on each function
                    acc = acc.flatMapLatest(x => func(x));
                }(series[i]));
            }

            return acc; 
        });
    }
}

Vous utilisez ensuite fromCallback pour le premier de la séquence et fromNodeCallback lors des appels successifs pour enchaîner les résultats dans la séquence.

1
Graham

Aujourd'hui, l'architecture javascript moderne n'est pas faite pour être synchrone, mais asynchrone, "pour une meilleure expérience utilisateur ...". Je veux dire que la requête http get est lancée, pendant ce temps, vous n'attendez pas que les données soient prêtes, mais l'exécution continue de votre programme. Une fois que vos données sont prêtes (demande xhr effectuée), vous êtes averti et capable de les utiliser. vous devez implémenter un service angulaire qui récupère les données, et en utilisant observable.

essentiellement un exemple de code pour votre vue, qui appelle le service responsable des requêtes http

    import { Component, OnInit } from '@angular/core';
    import { IntervalObservable } from "rxjs/observable/IntervalObservable";
    // my data model
    import { PositionsModel } from "./positionsmodel";
    // the service responsible to make the http requests
    import { MouvementService } from './mouvement.service';

    @Component({
      selector: 'app-mouvementview',
      template: '<div  *ngIf="data">  
                <div>{{data.x1r'}} </div>
                </div> ',
      styleUrls: ['./mouvementview.component.css']
    })

    export class MouvementviewComponent implements OnInit {
      public data: PositionsModel;
      private display : boolean;

      // Inject mouvementService
      constructor(private mouvementService: MouvementService) {
        this.display = false;
        this.alive = true;
        }

      ngOnInit() {
          this.mouvementService.getPositions()
          .first() // only gets fired once
          .subscribe((data) => {
            // this part of code will execute only when data will be retrieved. 
            this.data = data;
            this.display = true;
            /* Refresh your chart , NOTE : chart refresh will be done itself , if you are using some components with databing */
          });

     // if you need periodic refresh 
     // get our data every subsequent 200 mseconds
        IntervalObservable.create(200)
          .takeWhile(() => this.alive) // only fires when component is alive
          .subscribe(() => {
            this.mouvementService.getPositions()
              .subscribe(data => {
            console.log(data);
            this.data = data;
            /* refresh your chart */ 
              });
          });
     } 
  }

voici mon service Http:

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from "rxjs";
import 'rxjs/Rx';
import { PositionsModel } from "./positionsmodel";



@Injectable()
export class MouvementService      {

constructor(private http: Http) {}

getPositions(): Observable<PositionsModel> {
    // Make the HTTP request:
    console.log("launching http get request");
    return this.http
      .get('http://192.168.1.170:8080/api/mouvements')
      .map((res: Response) => {
        return res.json();
      });    
    }
}
0
sancelot