如何在浏览器端高效地处理巨大的javascript数组(20 mil坐标的数组)?
这是我的密码:如何在浏览器端高效地处理巨大的javascript数组(20 mil坐标的数组)?,javascript,arrays,performance,google-chrome,d3.js,Javascript,Arrays,Performance,Google Chrome,D3.js,这是我的密码: <!DOCTYPE html> <html> <head> <title>d3 Practice</title> </head> <body> <script src="./vislibs/d3.v3.min.js"></script> <canvas id="test" width="1024" height="768" style="border: 1
<!DOCTYPE html>
<html>
<head>
<title>d3 Practice</title>
</head>
<body>
<script src="./vislibs/d3.v3.min.js"></script>
<canvas id="test" width="1024" height="768" style="border: 1px solid black;"></canvas>
<script>
function generate_data(size){
var randomX = d3.random.normal(width/2 , 80),
randomY = d3.random.normal(height/2, 80);
var data = d3.range(size).map(function() {
return [
randomX(),
randomY()
];
});
return data
}
function main() {
var canvasEl = d3.select('#test').node();
// get 2d context to draw on
var ctx = canvasEl.getContext('2d');
width = canvasEl.width
height = canvasEl.height
data=generate_data(20000000)
alert("data generated")
// set fill color of context
var x = 0
ctx.fillStyle = 'red';
batch_size = 10000
debugger // Cannot step into requestAnimationFrame(draw_loop) at all , freezing eternity
draw_loop = function () {
if (x<=data.length-1) {
for (i in d3.range(0,batch_size)){
//console.log(x)
ctx.fillRect(data[x][0], data[x][1], 2, 2);
x = x+1
}
setTimeout(draw_loop,100);
}
}
requestAnimationFrame(draw_loop)
//alert("done reqanim")
}
main()
//init()
</script>
</body>
</html>
Javascript是一个单线程系统。本质上创建回调(如setTimeout)的函数仍然会杀死单线程系统 编辑: 根据评论,有一些创造性的方法可以让单线程Javascript产生异步执行大型逻辑函数的幻觉,从而释放执行单击处理程序之类的过程 @V3ss0n根据代码中进程冻结发生的位置,您有以下几个选项: 1) 进程在创建阵列期间冻结: 将20 mil项目加载到阵列可能需要一段时间,我建议不要这样做。有不止一种方法可以实现这一点,但我会尽可能多地将沉重的循环逻辑转移到javascript之外,并可能转移到web服务。这也有其自身的挑战,比如试图将2000万件物品发送回客户机,这是一个坏主意。但是,如果您可以将其划分为可管理的坐标组(最多20-100个),然后根据需要进行多个web服务调用(使用类似JQuery Ajax方法的方法),那么您的javascript所要担心的就是添加这些项。这将在一段时间内向您传输数据,因此不要期望它是即时的,但如果浏览器在20 mil时间内难以通过坐标创建功能循环,则不应冻结浏览器 2) 渲染每个单独的点时,进程冻结: 对此的修复更具创造性,如果您不想使用web服务,也可以用于上述过程。首先看下面的代码:
var renderingCounter = 0;
var asyncTimer = null; // keep the render timer global to prevent duplicate renders from being run
function StartRender(){
// test if existing async render is running
if(asyncTimer != null){
// existing async timer, clear the timer and clear the canvas
clearInterval(asyncTimer);
renderingCounter = 0;
// code for clearing the canvas ..
};
// create interval object
var asyncTimer = setInterval(function (){
// at the begining of each start render, test if all points have been rendered
if(renderingCounter >= pointArray.length){
// kill the asynctimer
clearInterval(asyncTimer);
asyncTimer = null;
renderingCounter = 0; // reset the counter
return;
};
while(renderingCounter < 100){ // only draw 100 items
if(renderingCounter >= pointArray.length){ // make sure you are not out of array bounds
return;
};
// execute display code for that point
}; // loop
}, 40);
}
var renderingCounter=0;
var asyncTimer=null;//使渲染计时器保持全局,以防止运行重复的渲染
函数StartRender(){
//测试现有异步渲染是否正在运行
if(asyncTimer!=null){
//现有异步计时器,清除计时器并清除画布
清除间隔(异步定时器);
渲染计数器=0;
//清除画布的代码。。
};
//创建间隔对象
var asyncTimer=setInterval(函数(){
//在每次开始渲染时,测试是否已渲染所有点
if(renderingCounter>=pointArray.length){
//杀死异步定时器
清除间隔(异步定时器);
asyncTimer=null;
renderingCounter=0;//重置计数器
返回;
};
而(renderingCounter<100){//仅绘制100个项目
如果(renderingCounter>=pointArray.length){//请确保没有超出数组边界
返回;
};
//执行该点的显示代码
};//循环
}, 40);
}
上面的代码将渲染过程拆分为100个点组。在完成一个组时,它会暂停40毫秒,这将在主线程上释放足够的时间,以允许继续执行其他项目(如UI)。全局变量renderingCounter将保持数组中的当前进度。如果函数在渲染过程中被重新调用,代码还会检查当前正在运行的渲染计时器(在本例中,它会终止当前渲染并重置它)。如果渲染计时器仍然导致挂起,可以增加暂停间隔
这两种实现异步执行的方法应该提供足够的灵活性,允许从服务器端web服务(如果您曾经这样做过)进行渲染或读取数据,同时保持流畅的ui,Javascript是一个单线程系统。本质上创建回调(如setTimeout)的函数仍然会杀死单线程系统 编辑: 根据评论,有一些创造性的方法可以让单线程Javascript产生异步执行大型逻辑函数的幻觉,从而释放执行单击处理程序之类的过程 @V3ss0n根据代码中进程冻结发生的位置,您有以下几个选项: 1) 进程在创建阵列期间冻结: 将20 mil项目加载到阵列可能需要一段时间,我建议不要这样做。有不止一种方法可以实现这一点,但我会尽可能多地将沉重的循环逻辑转移到javascript之外,并可能转移到web服务。这也有其自身的挑战,比如试图将2000万件物品发送回客户机,这是一个坏主意。但是,如果您可以将其划分为可管理的坐标组(最多20-100个),然后根据需要进行多个web服务调用(使用类似JQuery Ajax方法的方法),那么您的javascript所要担心的就是添加这些项。这将在一段时间内向您传输数据,因此不要期望它是即时的,但如果浏览器在20 mil时间内难以通过坐标创建功能循环,则不应冻结浏览器 2) 渲染每个单独的点时,进程冻结: 对此的修复更具创造性,如果您不想使用web服务,也可以用于上述过程。首先看下面的代码:
var renderingCounter = 0;
var asyncTimer = null; // keep the render timer global to prevent duplicate renders from being run
function StartRender(){
// test if existing async render is running
if(asyncTimer != null){
// existing async timer, clear the timer and clear the canvas
clearInterval(asyncTimer);
renderingCounter = 0;
// code for clearing the canvas ..
};
// create interval object
var asyncTimer = setInterval(function (){
// at the begining of each start render, test if all points have been rendered
if(renderingCounter >= pointArray.length){
// kill the asynctimer
clearInterval(asyncTimer);
asyncTimer = null;
renderingCounter = 0; // reset the counter
return;
};
while(renderingCounter < 100){ // only draw 100 items
if(renderingCounter >= pointArray.length){ // make sure you are not out of array bounds
return;
};
// execute display code for that point
}; // loop
}, 40);
}
var renderingCounter=0;
var asyncTimer=null;//使渲染计时器保持全局,以防止运行重复的渲染
函数StartRender(){
//测试现有异步渲染是否正在运行
if(asyncTimer!=null){
//现有异步计时器,清除计时器并清除画布
清除间隔(异步定时器);
渲染计数器=0;
//清除画布的代码。。
};
//创建间隔对象
var asyncTimer=setInterval(函数(){
//在每次开始渲染时,测试是否已渲染所有点
if(renderingCounter>=pointArray.length){
//杀死异步定时器
清除间隔(异步定时器);
asyncTimer=null;
function createPictureArray() {
var res, i, max_i;
var canvasEl = d3.select('#test').node();
var ctx = canvasEl.getContext('2d');
var data;
res = ctx.getImageData(0, 0, width, height);
data = res.data;
for (i = 0, max_i = data.length; i < max_i; i++) {
data[i] = 255;
}
return res;
}
function generateData(size) {
var i, x, y, s, res, data, randomX, randomY;
randomX = d3.random.normal(width/2 , 80);
randomY = d3.random.normal(height/2, 80);
res = createPictureArray();
data = res.data;
for (i = 0; i < size; i++) {
x = parseInt(randomX());
y = parseInt(randomY());
s = 4 * y * width + 4 * x;
if (data.length > s && data[s + 1] !== 0) {
data[s + 1] = 0;
data[s + 2] = 0;
}
}
return res;
}
function main() {
var canvasEl = d3.select('#test').node();
var ctx = canvasEl.getContext('2d');
width = canvasEl.width;
height = canvasEl.height;
data = generateData(2000000);
ctx.putImageData(data, 0, 0);
}
main()