Related
This is supposed to create to seperate charts. However when run they are identical and the values alternate between 0 and 1 for index, implying the index is changing (which confuses me idk why this would happen). Can anyone explain what it going on. I get the impression that somewhere i am mixing up the variables i use to reference the two charts, resulting in them being identical.
window.addEventListener(`load`, () => {
Chart.defaults.plugins.tooltip.enabled = false;
let data = {
labels: (() => {
let arr = [];
for(let i = 0;i < 31;i++) {
arr.push((30 - i) * -2);
}
return arr;
})(),
datasets: [
{
label: `first`,
data: (() => {let arr = [];for(let i = 0;i < 31;i++){arr.push(Math.random())}return arr})(),
borderColor: 'rgb(75, 192, 192)',
},
{
label: `second`,
data: (() => {let arr = [];for(let i = 0;i < 31;i++){arr.push(Math.random())}return arr})(),
borderColor: 'rgb(75, 0, 192)',
}
]
}
let graphs = [];
for(let i = 0;i < 2;i++) {
graphs.push(new lineGraph(data, data.labels.length));
}
setInterval(() => {
for(let i = 0;i < graphs.length;i++) {
graphs[i].updateData({first: [i], second: [i]})
}
}, 1000)
})
function lineGraph(data, maxDataSize) {
let div = document.createElement(`div`);
div.classList.add(`module`, `module-line`);
let canvas = document.createElement(`canvas`);
canvas.classList.add(`canvas-line`);
canvas.width = 200;
canvas.height = 200;
div.appendChild(canvas);
this.div = document.body.appendChild(div);
this.canvas = this.div.getElementsByClassName(`canvas-line`).item(0);
this.ctx = this.canvas.getContext(`2d`);
this.chart = new Chart(this.ctx, {
type: `line`,
data: data,
})
this.updateData = (data) => { // data => {datasetlabel: [data], ...}
this.chart.data.datasets.forEach((dataset, index) => {
let newData = data[dataset.label];
if(newData) {
if(dataset.data.length >= maxDataSize) {
for(let i = 0;i < (dataset.data.length - maxDataSize) + newData.length;i++) {
dataset.data.shift();
}
}
dataset.data.push(...newData);
}
});
this.chart.update();
}
return this;
}
Currently, inside setInterval, you alternately update both charts with y-values 0 and 1. Instead of these values, use Match.random(), same as you do when you initialize the chart data.
setInterval(() => {
for (let i = 0; i < graphs.length; i++) {
graphs[i].updateData({
first: [Math.random()],
second: [Math.random()]
})
}
}, 1000)
Please take a look at your amended code below and see how it works.
window.addEventListener(`load`, () => {
Chart.defaults.plugins.tooltip.enabled = false;
let data = {
labels: (() => {
let arr = [];
for (let i = 0; i < 31; i++) {
arr.push((30 - i) * -2);
}
return arr;
})(),
datasets: [{
label: `first`,
data: (() => {
let arr = [];
for (let i = 0; i < 31; i++) {
arr.push(Math.random())
}
return arr
})(),
borderColor: 'rgb(75, 192, 192)',
},
{
label: `second`,
data: (() => {
let arr = [];
for (let i = 0; i < 31; i++) {
arr.push(Math.random())
}
return arr
})(),
borderColor: 'rgb(75, 0, 192)',
}
]
}
let graphs = [];
for (let i = 0; i < 2; i++) {
graphs.push(new lineGraph(data, data.labels.length));
}
setInterval(() => {
for (let i = 0; i < graphs.length; i++) {
graphs[i].updateData({
first: [Math.random()],
second: [Math.random()]
})
}
}, 1000)
})
function lineGraph(data, maxDataSize) {
let div = document.createElement(`div`);
div.classList.add(`module`, `module-line`);
let canvas = document.createElement(`canvas`);
canvas.classList.add(`canvas-line`);
canvas.width = 200;
canvas.height = 40;
div.appendChild(canvas);
this.div = document.body.appendChild(div);
this.canvas = this.div.getElementsByClassName(`canvas-line`).item(0);
this.ctx = this.canvas.getContext(`2d`);
this.chart = new Chart(this.ctx, {
type: `line`,
data: data,
})
this.updateData = (data) => {
this.chart.data.datasets.forEach((dataset, index) => {
let newData = data[dataset.label];
if (newData) {
if (dataset.data.length >= maxDataSize) {
for (let i = 0; i < (dataset.data.length - maxDataSize) + newData.length; i++) {
dataset.data.shift();
}
}
dataset.data.push(...newData);
}
});
this.chart.update();
}
return this;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.6.0/chart.min.js"></script>
What is the best way to convert:
['[Title A]','child A','child B', '[Title B]', 'child C', 'child D']
to:
{
0: {
'title': 'Title A',
'children': ['child A', 'child B']
}
1: {
'title': 'Title B',
'children': ['Child C', 'Child D']
}
}
I have this so far which checks on the presence of brackets [] and I tried to add this to an object with a dynamic index which increments during a for loop every time a title with brackets is found:
let index = 0;
let listObject = {};
for (const listItem of listItems) {
const titleValue = listItem.match(/\[(.*?)\]/);
if (titleValue) {
++index;
listObject[index].title = titleValue[1];
} else {
listObject[index].children = [listItem];
}
}
console.log(listObject);
For the sake of simplicity let's first make an array of objects:
const res = arr.reduce((acc, cur) => {
const titleValue = cur.match(/\[(.*?)\]/)
titleValue ?
acc.push({
title: cur,
children: []
}) :
acc[acc.length - 1].children.push(cur)
return acc
}, [])
Now you can use the spread operator to have the nested object:
{...res}
const array = ['[Title A]','child A','child B', '[Title B]', 'child C', 'child D'];
let objToPush = {};
let objToSend = {};
array.map((d) => {
if (/^\[[^\]]+\]$/.test(d)) {
if (Object.keys(objToPush).length > 0) {
objToSend[Object.keys(objToSend).length] = { ...objToPush };
objToPush = {};
}
objToPush.title = d.substring(1, d.length - 1);
} else {
objToPush.children = objToPush.children ? [...objToPush.children, d] : [d]
}
});
objToSend[Object.keys(objToSend).length] = { ...objToPush };
console.log('objToPush', objToSend);
it worked for me (JSFiddle https://jsfiddle.net/wepbzdfL/48/)
Just updated yours so the logic is sound. Can see what you tried.
Read up on creating new objects and arrays in JS, and when you can add to them.
let listItems = ['[Title A]', 'child A', 'child B', '[Title B]', 'child C', 'child D'];
let index = 0;
var listObject = {};
for (const listItem of listItems) {
const isTitle = listItem[0] == "[" && listItem[listItem.length - 1] == "]"
if (isTitle) {
++index;
listObject[index] = {
title: listItem.substring(1, listItem.length -1),
children: [] //Create Array
}; //Create Object
} else {
listObject[index].children.push(listItem); //Add to children array
}
}
console.log(listObject);
To add on why I used an index lookup, instead of regex,
Run this:
var testArray = [];
var arrayCount = 20000000;
var regexMatch = /\[(.*?)\]/;
for (var i = 0; i < arrayCount; i++) {
testArray.push("[" + makeid(Math.round(Math.random() * 10)) + "]")
}
console.log(testArray.length);
var start = new Date();
console.log(start.toString());
for (var i = 0; i < arrayCount; i++) {
var testItem = testArray[i];
if (testItem.match(regexMatch)) {
} else {
}
}
console.log("regex took " + (new Date().getTime() - start.getTime()) / 1000 + " seconds");
start = new Date();
for (var i = 0; i < arrayCount; i++) {
var testItem = testArray[i];
if (testItem[0] === "[" && testItem[testItem.length - 1] === "]") {
} else {
}
}
console.log("index lookup took " + (new Date().getTime() - start.getTime()) / 1000 + " seconds");
function makeid(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for (var i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
I don't know if it's the "best" way, but this is my solution:
const array = [
"[Title A]",
"child A",
"child B",
"[Title B]",
"child C",
"child D"
];
let index = -1;
const res = array.reduce((acc, curr) => {
if (/^\[[^\]]+\]$/.test(curr)) {
acc = {
...acc,
[++index]: { title: curr.substring(1, curr.length - 1), children: [] }
};
} else {
acc[index].children = [...acc[index].children, curr];
}
return acc;
}, {});
console.log(res);
I need to center the trace because on load the trace doesn't appears and the user should move the view.
I couldn't fin any documentation or question referencing this issue.
The data is retrieved from the server via service and
I'm using Angular 7 for the front-end and plotly to draw the plot.
When the page loads the plot looks like this: Image error.
If i move the view looks like this: Image okey.
Thanks you
Sample code:
private loadPlot(): void {
const minValues = [];
const maxValues = [];
const dataForPlot = [];
let traceIndex = 0;
for (const key in this.serverData.data) {
if (key === 'filename') {
continue;
}
const values = [];
const colorsForLine = [];
const markersForLine = [];
const mean = calculateMean(this.serverData.data[key]);
const textArray = [];
this.serverData.data[key].forEach(
(element, index) => {
let marker = 'circle';
const color = getPointColor(element['nc']);
const elementText = element['value'];
const value = element['value'];
if (index === this.serverData.data[key].length - 1) {
marker = 'diamond-cross';
}
values.push(value);
colorsForLine[index] = color;
markersForLine[index] = marker;
textArray[index] = elementText + '<br>' + truncateFilename(this.serverData.data['filename'][index], 50);
}
);
minValues.push(Math.min.apply(null, values.filter((n) => !isNaN(n))));
maxValues.push(Math.max.apply(null, values.filter((n) => !isNaN(n))));
const trace = {
x: this.serverData.dates,
y: values,
type: 'scatter',
mode: 'lines+markers',
marker: {
color: colorsForLine,
symbol: markersForLine,
size: 5
},
line: {
// color: colorsForLine,
},
connectgaps: false,
name: key,
description: 'number of ' + key,
filenames: this.serverData.data['filename'],
hoverinfo: 'x+text',
hovertext: textArray
};
dataForPlot.push(trace);
traceIndex++;
}
let MINVALUEFORPLOT;
let MAXVALUEFORPLOT;
if (this.plotThreshold === undefined) {
MINVALUEFORPLOT = Math.min.apply(null, minValues) - Math.abs((Math.min.apply(null, minValues) * 0.1));
MAXVALUEFORPLOT = Math.max.apply(null, maxValues) + (Math.max.apply(null, maxValues) * 0.1);
} else {
const height = (this.layoutShapes[this.layoutShapes.length - 1]['y0'] - this.layoutShapes[this.layoutShapes.length - 1]['y1']) * 0.3;
MINVALUEFORPLOT = this.layoutShapes[this.layoutShapes.length - 1]['y1'] - height;
MAXVALUEFORPLOT = this.layoutShapes[this.layoutShapes.length - 1]['y0'] + height;
}
this.layout = {
// title: this.chart.name,
title: this.generatePlotTitle(),
shapes: [],
colorway: traceColor.colorRange,
hovermode: 'closest',
xaxis: {
nticks: 10,
},
yaxis: {
type: 'linear',
range: [MINVALUEFORPLOT, MAXVALUEFORPLOT]
},
currentDiv: 'plot'
};
this.layout.shapes = this.layoutShapes;
Plotly.react('plot', dataForPlot, this.layout);
}
When extending traces with plotly the points are drawn in the past. As you can see in the picture, any
Picture of the problem
The chart now consists of 2 parts one on the right is data from the database, and to the left is data being plotted in "real-time". The data that is being plotted in realtime is to the left of the data from the database is even though the timestamps are after the ones from the databases.
The time stamp on the console logs is correct but plotly is placing them at the wrong time on the x-axis. The should be drawn at the end of the graph.
updateData (sensorData) {
if (!this.initiated || !this.isLiveData) {
return
}
let y = []
let i = 0
let x = []
let traces = []
for (let sensor in this.configuration) {
for (let signal in this.configuration[sensor]) {
let xDate = new Date(sensorData[sensor].timestamp)
if (sensorData.hasOwnProperty(sensor) && sensorData[sensor].hasOwnProperty(signal)) {
y.push([sensorData[sensor][signal]])
x.push([xDate.getTime()])
}
// x time seems to be good here
console.log('Update data', xDate.getTime(), xDate)
traces.push(i)
i++
}
}
console.log('Plotting', y, x, this.widget.getXRange())
if (y.length > 0) {
this.$plotly.extendTraces('plotly-chart', {
y: y,
x: x
}, traces)
}
},
The data from the db is added with following code.
updateFromDb (sensorData) {
let data = []
let x = []
let yData = {}
for (let sensor in this.configuration) {
for (let i = 0, len = sensorData[sensor].length; i < len; i++) {
let timestamp = sensorData[sensor][i].timestamp
x.push(timestamp)
for (let source in this.configuration[sensor]) {
let name = sensor + ' ' + getSignalName(source, this.angleLabel)
if (!yData.hasOwnProperty(name)) {
yData[name] = {
data: [],
color: this.configuration[sensor][source].color
}
}
if (!sensorData[sensor][i].hasOwnProperty(source)) {
yData[name].data.push(0)
} else {
yData[name].data.push(sensorData[sensor][i][source])
}
if (this.configuration[sensor][source].hasOwnProperty('yaxis')) {
yData[name]['yaxis'] = 'y' + this.configuration[sensor][source].yaxis
}
}
}
}
for (let name in yData) {
let sensorData = {
name: name,
x: x,
y: yData[name].data,
type: 'line',
mode: 'lines',
line: {
width: 2,
color: yData[name].color
},
marker: {
width: 2,
color: yData[name].color
}
}
if (yData[name].hasOwnProperty('yaxis')) {
sensorData['yaxis'] = yData[name].yaxis
}
data.push(sensorData)
}
this.$plotly.react(
document.getElementById('plotly-chart'),
data,
this.getLayout(false),
this.chartProperties
)
}
There is also a function that scroll the window over xaxis every 50 ms to make it look smooth.
windowScroller () {
if (!this.initiated || !this.isLiveData) {
return
}
let timeDifference = 0
if (this.timeDifference !== null) {
timeDifference = this.timeDifference
}
/**
* Make sure the line never gets behind the scroller.
*/
if (Object.keys(this.latestPoint).length > 0) {
let latestTime = this.latestPoint[Object.keys(this.latestPoint)[0]].timestamp
let scrollDiff = new Date().getTime() - latestTime
if (scrollDiff !== this.scrollDelay && this.lastDelayTime !== latestTime && scrollDiff < 60000) {
this.lastDelayTime = latestTime
this.scrollDelay = scrollDiff
// console.log('update scroll', scrollDiff, 'from', latestTime)
}
}
let currentTime = new Date().getTime() - timeDifference - this.scrollDelay
let firstTime = new Date().getTime() - this.getMilisecondsFromMinutes(this.widget.getXRange()) - timeDifference - this.scrollDelay
let relayoutPromise = new Promise((resolve, reject) => {
this.$plotly.relayout('plotly-chart', {
xaxis: {
color: '#fff',
type: 'date',
range: [firstTime, currentTime]
}
})
})
relayoutPromise.then(() => {
console.log('relayout')
})
let data = document.getElementById('plotly-chart').data
// We calculate the max points using 4 hertz
let maxPoints = (this.getSecondsFromMinutes(this.widget.getXRange()) + 10) * this.widget.getRefreshRate()
if (this.minMax) {
maxPoints = maxPoints * 2
}
for (let i in data) {
if (data[i].y.length >= maxPoints) {
data[i].y.shift()
data[i].x.shift()
}
}
}
I am currently learning to use Electron for a university project. What I am trying to do is to read data from a .xlsx file, then create a Chart with chart.js and display the data. For that, I am using exceljs and chart.js. For this, I wrote multiple functions (Code below). My problem right now is, that I try to return an Array with some data (It gets created perfectly fine) but it just displays as undefined in the other function.
I know, my code is not good. It's complete spaghetti, honestly. I just need to get this working, it doesn't have to be nice code.
This is the function I use to draw the chart:
function createGraph() {
// Create Canvas if not already created
if (document.getElementById('datacanvas') == null) {
var canvas = document.createElement('canvas');
canvas.setAttribute("id", "datacanvas")
var datadiv = document.getElementById("datadiv");
datadiv.appendChild(canvas);
var ctx = canvas.getContext("2d");
}
else {
var canvas = document.getElementById('datacanvas')
var ctx = canvas.getContext("2d");
}
var labls = ["Januar", "Februar", "März", "April", "Mai", "Juni",
"Juli", "August", "September", "Oktober", "November", "Dezember"];
var datasts = createDatasets();
console.log(datasts);
var chart = new Chart(ctx, {
type: 'line',
data: {
labels: labls,
datasets: datasts
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
}
The problem I have is the varible datasts or rather, the data property because the data is undefined. It gets created with these two functions:
function createDatasets() {
console.log("createDatasets");
var dataset = [];
var rdbStrom = document.getElementById('rdbStrom');
var rdbGas = document.getElementById('rdbGas');
var rdbWasser = document.getElementById('rdbWasser');
var rdbGesamt = document.getElementById('rdbGesamt');
if (rdbStrom.checked) {
var set = {
label: 'Stromkosten',
data: getDataArray("strom"),
borderColor: '#FF0000',
borderWidth: 1
};
dataset.push(set);
}
if (rdbGas.checked) {
var set = {
label: 'Gaskosten',
data: getDataArray("gas"),
borderColor: '#00FF00',
borderWidth: 1
};
dataset.push(set);
}
if (rdbWasser.checked) {
var set = {
label: 'Wasserkosten',
data: getDataArray("wasser"),
borderColor: '#0000FF',
borderWidth: 1
};
dataset.push(set);
}
if (rdbGesamt.checked) {
;
var set = {
label: 'Gesamtkosten',
data: getDataArray("gesamt"),
borderColor: '#FFFFFF',
borderWidth: 1
};
dataset.push(set);
}
// Wait
setTimeout(() => {
return dataset;
}, 1000);
}
function getDataArray(type) {
console.log("getDataArray");
var data = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
var period = [];
var start = new Date(document.getElementById('start').value);
var end = new Date(document.getElementById('end').value);
var tmp = new Date(start);
do {
period.push(tmp.toDateString());
tmp.setDate(tmp.getDate() + 1);
} while (tmp <= end)
if (!fs.existsSync('./Data.xlsx')) {
alert("Error finding File 'Data.xlsx'.");
}
else {
switch (type) {
case "strom": {
workbook.xlsx.readFile('./Data.xlsx')
.then(function () {
var worksheet = workbook.getWorksheet('Data');
for (var i = 2; i <= worksheet.rowCount; i++) {
var r = worksheet.getRow(i);
var d = new Date(r.getCell(1).value);
if (period.includes(d.toDateString())) {
var vbr = r.getCell(3).value;
var prc = r.getCell(4).value;
var gprc = r.getCell(5).value;
var tax = r.getCell(6).value;
var kosten = (vbr * prc) + gprc + tax;
data[d.getMonth()] = data[d.getMonth()] + kosten;
}
}
})
break;
}
case "gas": {
workbook.xlsx.readFile('./Data.xlsx')
.then(function () {
var worksheet = workbook.getWorksheet('Data');
for (var i = 2; i <= worksheet.rowCount; i++) {
var r = worksheet.getRow(i);
var date = new Date(r.getCell(1).value);
if (period.includes(date.toDateString())) {
var vbr = r.getCell(8).value;
var prc = r.getCell(9).value;
var gprc = r.getCell(10).value;
var tax = r.getCell(11).value;
var kosten = (vbr * prc) + gprc + tax;
data[d.getMonth()] = data[d.getMonth()] + kosten;
}
}
})
break;
}
case "wasser": {
workbook.xlsx.readFile('./Data.xlsx')
.then(function () {
var worksheet = workbook.getWorksheet('Data');
for (var i = 2; i <= worksheet.rowCount; i++) {
var r = worksheet.getRow(i);
var date = new Date(r.getCell(1).value);
if (period.includes(date.toDateString())) {
var vbr = r.getCell(13).value;
var prc = r.getCell(14).value;
var gprc = r.getCell(15).value;
var tax = r.getCell(16).value;
var kosten = (vbr * prc) + gprc + tax;
data[d.getMonth()] = data[d.getMonth()] + kosten;
}
}
})
break;
}
default:
break;
}
}
// Wait till process is done reading file
setTimeout(() => {
console.log("Timeout")
for (i = 0; i < data.length; i++) {
console.log("Data[" + i + " ]: " + data[i]);
}
console.log("Return DataArray");
return data;
}, 1000);
}
Again, I know my code is not good, I just need to get this working.
The output in the console is the following:
createDataset
getDataArray
undefined // This is the datasts Variable which I need to wait for
Timeout // This comes from the third function
// Here it displays the data it read from the Excel file from the third function
This is because of asynchronicity issue, you use createDatasets as if it was a synchronous function (such as return 1 + 2) while it relies on asynchronous operations,
ie :
let exampleInt = 0
setTimeout(() => {
// callback
exampleInt = 1;
return dataset;
}, 1000);
// This will be reached before the callback executes, so exampleInt equals 0
You should have a look at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises to achieve your goal which is, I guess, wait the end of an operation before executing code based on the result of that operation
Does it brighten your mind ?
Now resolution, first function : createDatasets
function createDatasets() {
console.log("createDatasets");
var dataset = [];
var rdbStrom = document.getElementById('rdbStrom');
var rdbGas = document.getElementById('rdbGas');
var rdbWasser = document.getElementById('rdbWasser');
var rdbGesamt = document.getElementById('rdbGesamt');
// storing each label we need
let dataArraysNeeded = [];
let dataArraysNeededAsPromises = [];
let designParams = {
"strom": {
title: "Stromkosten",
color: "#FF0000"
},
"gas": {
title: "Gaskosten",
color: "#00FF00"
},
"wasser": {
title: "Wasserkosten",
color: "#0000FF"
},
"gesamt": {
title: "Gesamtkosten",
color: "#FFFFFF"
}
};
if (rdbStrom.checked) {
dataArraysNeeded.push('strom');
}
if (rdbGas.checked) {
dataArraysNeeded.push('gas');
}
if (rdbWasser.checked) {
dataArraysNeeded.push('wasser');
}
if (rdbGesamt.checked) {
dataArraysNeeded.push('gesamt');
}
// From here we have an array of labels (ex: ["wasser","gesamt"])
// We now want to get the data array for each of these labels, here is how it's done
for (let i = 0; i < dataArraysNeeded.length; i++) {
dataArraysNeededAsPromises.push(getDataArray(dataArraysNeeded[i]));
}
// This will execute all the promises AND WAIT the end of the slowest promise
return Promise.all(dataArraysNeededAsPromises).then((sets) => {
let currentLabel = "";
// sets[0] equals getDataArray("wasser") for example
for (let j = 0; j < sets.length; j++) {
currentLabel = dataArrayLabel[j]; // "wasser"
dataset.push( {
label: designParams[currentLabel]["title"],
data: sets[j],
borderColor: designParams[currentLabel]["color"],
borderWidth: 1
});
}
return dataset; // Array of objects {label, data, borderColor, borderWidth}
});
}
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all for details on how works Promise.all
Your main function createGraph which calls createDatasets (you can see how the result of a function returning a promise is consumed)
function createGraph() {
// Create Canvas if not already created
if (document.getElementById('datacanvas') == null) {
var canvas = document.createElement('canvas');
canvas.setAttribute("id", "datacanvas")
var datadiv = document.getElementById("datadiv");
datadiv.appendChild(canvas);
var ctx = canvas.getContext("2d");
}
else {
var canvas = document.getElementById('datacanvas')
var ctx = canvas.getContext("2d");
}
var labls = ["Januar", "Februar", "März", "April", "Mai", "Juni",
"Juli", "August", "September", "Oktober", "November", "Dezember"];
// Here you instanciate your promise of Dataset, which IS NOT synchronous
var datasetPromise = createDatasets();
// So you need to specifiy a callback, executed on promise completion
return datasetPromise.then((yourDatasetReadyToBeUsed) => {
var chart = new Chart(ctx, {
type: 'line',
data: {
labels: labls,
datasets: yourDatasetReadyToBeUsed
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
return 'completed !';
});
}
I'll let you find the last one as it's very similar to those two functions (getDataArray needs to return a Promise too as it reads a file) !
Is it more clear for you ?