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