我到达了一个阶段,我认为我不了解事件的正确回调。我正在使用d3.js v5,我的问题如下。
我有一个解析tsv文件并使用selectedData
函数将数据添加到全局数组d3.tsv
的函数,该函数利用函数addData
中的promise:
//function takes filename and data description to add to the selectedData global variable
function addData(filename, dataDesc) {
d3.tsv(filename, type).then ((data) => {
data.forEach(e => {
var obj = {
Title: dataDesc,
ID: e.ID,
Value: e.Value,
}
selectedData.push(obj);
});
});
}
function type(d) {
d.Value = +d.Value;
}
现在,我使用此函数添加单个数据文件,或者可以多次调用它以将多个数据文件中的数据添加到此全局变量中。全局变量用于许多其他功能,但首先用于生成条形图。
对于添加单个文件,我想在将数据添加到全局变量后生成条形图。为此,我使用以下功能:
//functions adds one data file
function addOneData() {
filename = userfile; //gets the user file name based on <input> element using the FileReader api
dataDesc = document.getElementById('file_title').value; //gets the Descriptive Title of the data
addData(filename, dataDesc); //add data to the global variable of selectedData
drawBarChart(); //draws a grouped bar chart based on the global array of selectedData
}
我有一个类似的功能,它可以循环遍历多个数据文件的数组,并将它们添加到具有不同filenames
和dataDesc
的全局变量中。对于多个文件,我不想在每个文件之后进行绘制,这就是drawBarChart
函数不属于addData
函数一部分的原因。
现在,我的问题是addData
和drawBarChart
的功能正在异步运行,因此drawBarChart
在将数据添加到selectedData
全局变量之前运行了很多。所以我的问题是,我该如何使其同步或使用诺言的力量?我一直在尝试Promise和async / await,但感觉好像我错过了一些关键概念。任何帮助表示赞赏。谢谢。
答案 0 :(得分:5)
“创建数据的同步加载” 在这种情况下没有多大意义...取而代之的是,接受异步代码的性质。
您还说过:
对于多个文件,我不希望在每个文件之后进行绘制,这就是为什么drawBarChart函数不是addData函数的一部分的原因。
但这就是您的addOneData
函数当前正在做的事情:它正在为每个文件调用drawBarChart
(由于addData
的异步特性,这是错误的)。
因此,让我们解决两个问题,即异步代码和在加载多个文件后调用drawBarChart
。
一个简单的解决方案是将drawBarchart
作为回调方法传递给另一个then
方法,如下所示:
function addData(filename, dataDesc, callback) {
d3.json(filename).then((data) => {
data.forEach(e => {
var obj = {
Title: dataDesc,
Value: e
}
selectedData.push(obj);
});
}).then(callback)
};
这是一个演示,其中包含一个我在线创建的非常简单的JSON(需要d3.json
,但一般原理与您的d3.tsv
相同,并且可以作为您的案例的TSV):
var selectedData = [];
addOneData();
function addData(filename, dataDesc, callback) {
d3.json(filename).then((data) => {
data.forEach(e => {
var obj = {
Title: dataDesc,
Value: e
}
selectedData.push(obj);
});
}).then(callback)
};
function addOneData() {
filename = "https://api.myjson.com/bins/m1vsl";
dataDesc = "FooBar"
addData(filename, dataDesc, drawBarChart);
};
function drawBarChart() {
console.log(selectedData)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
如前所述,您可能有许多文件,但您只想调用drawBarChart
。在这种情况下,请使用Promise.all
和带有文件名的数组:
function addData(filenames, dataDesc, callback) {
Promise.all(filenames.map(d => d3.json(d))).then((alldata) => {
alldata.forEach(data => {
data.forEach(e => {
var obj = {
Title: dataDesc,
Value: e
}
selectedData.push(obj);
});
});
}).then(callback)
};
这是演示:
var selectedData = [];
addAllData();
function addData(filenames, dataDesc, callback) {
Promise.all(filenames.map(d => d3.json(d))).then((alldata) => {
alldata.forEach(data => {
data.forEach(e => {
var obj = {
Title: dataDesc,
Value: e
}
selectedData.push(obj);
});
});
}).then(callback)
};
function addAllData() {
filenames = ["https://api.myjson.com/bins/m1vsl", "https://api.myjson.com/bins/rncvp"];
dataDesc = "FooBar";
addData(filenames, dataDesc, drawBarChart);
};
function drawBarChart() {
console.log(selectedData)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>