使用D3.js创建数据的同步加载并在加载后进行绘制

Coo*_*ola 1 javascript asynchronous promise async-await d3.js

我到达了一个阶段,我认为我不了解事件的正确回调。我正在使用d3.js v5,我的问题如下。我有一个解析tsv文件并selectedData使用该d3.tsv函数利用函数中的Promise将数据添加到全局数组的函数addData

//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;
}
Run Code Online (Sandbox Code Playgroud)

现在,我使用此函数添加单个数据文件,或者可以多次调用它以将多个数据文件中的数据添加到此全局变量中。全局变量用于许多其他功能,但首先用于生成条形图。

对于添加单个文件,我想在将数据添加到全局变量后生成条形图。为此,我使用以下功能:

//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
}
Run Code Online (Sandbox Code Playgroud)

我有一个类似的功能,它循环遍历多个数据文件的数组,并将它们添加到具有不同的filenames和的全局变量中dataDesc。对于多个文件,我不希望在每个文件之后进行绘制,这就是为什么该drawBarChart功能不属于该addData功能的原因。

现在我的问题是,职能addDatadrawBarChart被异步运行,因此drawBarChart运行多前的数据添加到selectedData全局变量。所以我的问题是,我该如何使其同步或使用诺言的力量?我一直在尝试Promise和async / await,但感觉好像我错过了一些关键概念。任何帮助表示赞赏。谢谢。

Ger*_*ado 5

在这种情况下,“创建数据的同步加载”意义不大……相反,应接受异步代码的性质。

另外,您说:

对于多个文件,我不希望在每个文件之后进行绘制,这就是为什么drawBarChart函数不是addData函数的一部分的原因。

但这就是您的addOneData函数现在正在做的事情:它正在drawBarChart为每个文件调用(如您所知,由于的异步特性,这是错误的addData)。

因此,让我们解决两个问题,异步代码和drawBarChart加载多个文件后的调用。

1.异步代码

一个简单的解决方案是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)
};
Run Code Online (Sandbox Code Playgroud)

这是一个带有我在网上创建的非常简单的JSON的演示(需要d3.json,但一般原理与您相同,d3.tsv并且就您的情况而言,它就像TSV一样工作):

function addData(filename, dataDesc, callback) {
  d3.json(filename).then((data) => {
    data.forEach(e => {
        var obj = {
            Title: dataDesc,
            Value: e
        }
        selectedData.push(obj);
    });
  }).then(callback)
};
Run Code Online (Sandbox Code Playgroud)
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)
}
Run Code Online (Sandbox Code Playgroud)

2.多个文件

如前所述,您可能有许多文件,但您只想调用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)
};
Run Code Online (Sandbox Code Playgroud)

这是演示:

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
Run Code Online (Sandbox Code Playgroud)
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)
};
Run Code Online (Sandbox Code Playgroud)