Javascript 处理进程事件上的分块JSON?

Javascript 处理进程事件上的分块JSON?,javascript,json,d3.js,xmlhttprequest,chunked-encoding,Javascript,Json,D3.js,Xmlhttprequest,Chunked Encoding,我有一个服务器请求,它可能会返回一个巨大的json列表(~100K条记录,~50MB),其中包含我必须使用D3js在画布上绘制的点。我想在它们到达时绘制它们,以利于互动和节省内存,因此: 我在服务器端启用了分块传输编码 +我在客户端尝试了这一点: d3.json('?json=qDefects&operationid=' + opid) // my request .on("load", function (json) { draw(json); // this works

我有一个服务器请求,它可能会返回一个巨大的json列表(~100K条记录,~50MB),其中包含我必须使用D3js在画布上绘制的点。我想在它们到达时绘制它们,以利于互动和节省内存,因此:

我在服务器端启用了分块传输编码 +我在客户端尝试了这一点:

d3.json('?json=qDefects&operationid=' + opid) // my request 
  .on("load", function (json) {
    draw(json); // this works, but only after a long delay that I'd avoid...
  })
  .on("progress", function (json) {
    draw(json); // but this fails : json is not yet available here
  })
  .get();
是否可以在加载JSON时将其分块处理?它是否有助于以不同的方式构造JSON数据?目前它是一个单一的数组,所以我有

[{"x":1, "y":2},{"x":2, "y":3}, // chunk 1
...
{"x":6845, "y":239426},{"x":51235, "y":234762}] // last chunk

将点分成更小的数组是否有帮助?

tl;dr:无法使用
progress
事件操纵JSON


首先,您可能正在使用(D3V3和v4),而不是(D3V5)。这是一个重要的区别,因为在两个微库中,该方法具有相同的名称,即
d3.json
。然而,
d3.json
在前者中是一个XMLHttpRequest,而在后者中是一个承诺

第二,这是最重要的,这似乎是(不幸的)一个错误。你说“我想在它们到达时绘制它们,以利于交互性和节省内存”,但问题是你不能:即使在数据到达时你可以操作数据(你不能,见下文),D3也只能在XHR(或承诺)下载完数据后才开始绘制任何东西。这意味着,有了50MB的数据,用户会盯着空白页面看几秒钟。。。因此,这里最好的建议是重新考虑数据文件和整个datavis的大小

回到问题上来:

progress
事件仅用于监视进度。根据报告:

本规范定义了一个事件接口ProgressEvent,可用于测量进度。(强调矿山)

我们可以在下面的演示中检查这一点(我正在使用一个数组,其中包含您在问题中共享的对象,我只是复制/粘贴了几次相同的对象)。我们可以使用
srcielement.response
查看加载的JSON,但无法更改:

d3.json(“https://api.myjson.com/bins/1d7yoi")
.关于“进展”,职能(d){
console.log(d.srcielement.response)
})
.on(“加载”,函数(){
控制台日志(“完成”)
})
.get()
查看随附的小提琴:

虽然前面的答案是正确的,因为您不能修改进度事件,但是您可以做一件简单的事情,调用一个外部变量。因此,下面的代码将允许您重新处理字符串并将其发送到d3

var x = ''
d3.json("https://api.myjson.com/bins/1d7yoi")
  .on("progress", function(d) {
    x = d.responseText
    x = "ehllo" + x;
    console.log(x)
  })
  .on("load", function() {
    console.log("done")
  })
  .get()

您可以将responseText分配给变量x,并根据需要对x进行操作

多亏了前面的答案,我最终得出以下结论:

function progressLoad(f) {
  let start = 0;
  return function (event) {
    let str = event.responseText.substr(start); 
    let i = str.indexOf("{");                  
    let j = str.lastIndexOf("}");
    str = "[" + str.substr(i, j) + "]";
    let data = JSON.parse(str);
    f(data);
    start = start + j + 1;
  }
}

d3.json('?json=qDefects&operationid=' + opid)
  .on("progress", progressLoad(draw));
它在我没有嵌套{}的(简单)情况下工作得很好。 但是,我也确保我的服务器提供了与我请求的每个记录相对应的块,并且它看起来responseText也由这些块递增,所以我总是在str中有匹配的{}


当然,这仍然会生成一个很长的、无用的responseText,也可能是一个无用的最终json解析(即使我没有“加载”事件?),但我现在可以处理这个问题。

如果您的服务器正在分块一个最终有效的json响应,您需要在将其发送到DrawThank@EricYang之前手动处理进程中的无效json。@EricYang说:“我没有意识到json在“进程”中是可用的,我们将看到如何处理它……我不确定是否可以在进程中操纵数据。”。如果可能的话,最好有一个答案(然后我会删除我的答案)。请参阅下面发布的答案。如果您没有操作数据,请参阅下面我的注释。您只是将全局值与异步函数的返回值相关联,这肯定是一种反模式。除此之外,您没有操作数据:您只是每秒对字符串(而不是任何对象)进行几次相同的更改。该示例旨在说明您可以对进度处理程序发出的值进行操作。我可以很容易地获取一个部分无效的json字符串,“修复”它,并将其发送给d3进行绘图。至于它是否是一个全局变量,我也可以将它封装在一个函数中,这样它就不会污染全局范围,而不需要像OP的用例那样“操纵”响应对象。你想要的是得到一个部分的回应,然后把它发送出去进行策划,我的回答就是这样。不,不是这样。首先,
d3.json
回调必须等待XHR完成。其次,这是一个字符串,每秒加载几次:您必须一次又一次地解析(相同)不断增长的字符串,并将其发送到另一个函数,而不是发送到
d3.json
回调函数。最终,这将需要更多的时间。只要设置一个D3代码来尝试你的解决方案,你就会明白。虽然你所说的一切都是正确的,但我认为它没有抓住要点。这是总渲染时间与第一点可见时间之间的差异。我的目标是使第一个数据点的渲染时间最短。是的,您不能依赖于回调,但我认为调用另一个渲染函数没有错。至于一遍又一遍地解析同一个不断增长的字符串,我可以很容易地为前一个字符串的长度维护一个变量,适当地截断,修复该字符串,使其成为正确的json,然后继续我的工作