2013-08-23 3 views
2

Я немного застрял, я создал приложение D3, которое извлекает связку данных датчиков из базы данных. Я сделал это так, чтобы он переходил и работал на 30-секундном цикле для обновления информации. Он анализирует объект Javascript и отображает линию для каждого датчика на графике. Кажется, все идет хорошо, но через пару часов в приложении остановится и перестанет обновляться. Затем он жалуется, что скрипт не отвечает. Вот JS для сюжета:Не удается найти утечку памяти D3.js

var thresholdTemp = 72; 
var minutes = 5; 
var transInterval; 

//Main function to create a graph plot 
function plot(date1, date2, interval) { 
     var data; 
     //If we define a date search parameter then we don't want to have it load   interactive 
     if (date1 == undefined && date2==undefined) { 
       data = loadMinutesJSON(minutes); 
     } else { 
       data = searchJSON(date1, date2, interval); 

     } 
     var margin = {top: 20, right: 80, bottom: 60, left: 50}, 
     width = 960 - margin.left - margin.right , 
     height = 500 - margin.top - margin.bottom; 

     //Re-order the data to be more usable by the rest of the script 
     var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse; 
     //Convert the JSON to be better usable by D3 
     data = convertJSON(data); 
     //Nest the data so that it's grouped by serial number 
     data = d3.nest().key(function(d) { return d.serial; }).entries(data); 

     //Set the X domain to exist within the date and times given 
     var x = d3.time.scale().domain([getMinDate(data), getMaxDate(data)]).range([0, (width)]); 


     //Y Axis scale set to start at 45 degrees and go up to 30 degrees over highest temp 
     var y = d3.scale.linear() 
      .domain([ 
       45, 
       getMaxTemp(data) + 10 
      ]) 
      .range([height, 0]); 


     //Set up the line colors based on serial number, this generates a color based on an ordinal value 
     var color = d3.scale.category20() 
         .domain(d3.keys(data[0]).filter(function(key) { return key === 'serial';})); 

     //Define where the X axis is  
     var xAxis = d3.svg.axis() 
      .scale(x) 
      .orient("bottom") 
      .tickFormat(d3.time.format("%b %d %H:%M:%S")); 

     //Define where the Y axis is 
     var yAxis = d3.svg.axis() 
      .scale(y) 
      .orient("left"); 

     //When called creates a line with the given datapoints 
     var line = d3.svg.line() 
      .interpolate("basis") 
      .x(function(d) { return x(d.date); }) 
     y(function(d) { return y(d.reading); }); 

     //An extra line to define a maximum temperature threshold 
     var threshold = d3.svg.line() 
      .x(function(d) { return x(d.date);}) 
      .y(function(d) { return y(thresholdTemp); }); 

     //Append the SVG to the HTML element 
     var svg = d3.select("#plot").append("svg") 
      .attr("width", (width + margin.left + margin.right)) 
      .attr("height", height + margin.top + margin.bottom) 
      .append("g") 
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 

     //Define the clipping boundaries 
     svg.append("defs").append("clipPath") 
      .attr("id", "clip") 
      .append("rect") 
      .attr("width", (width + margin.left + margin.right)) 
      .attr("height", height + margin.top + margin.bottom); 

     //Add the X axis   
     svg.append("g") 
       .attr("class", "x axis") 
       .attr("transform", "translate(0," + height + ")") 
       .call(xAxis) 
       .selectAll("text") 
       .style("text-anchor", "end") 
       .attr("dx", "-.8em") 
       .attr("dy", ".15em") 
       .attr("transform" , function (d) {return "rotate(-35)"}); 

     //Add the Y axis and a label denoting it as temperature in F  
     svg.append("g") 
       .attr("class", "y axis") 
       .call(yAxis) 
       .append("text") 
       .attr("transform", "rotate(-90)") 
       .attr("y", 6) 
       .attr("dy", ".71em") 
       .style("text-anchor", "end") 
       .text("Temperature (F)"); 

     //Create the lines based on serial number 
     var serial = svg.selectAll(".serial") 
       .data(data, function(d) { return d.key;}) 
       .enter().append("g") 
       .attr("class", "serial"); 
     //Add the extra line for a threshold and align it with the current time 
     var threshElement = svg.selectAll(".thresh") 
       .data(data, function(d) { return d.key;}) 
       .enter().append("g") 
       .attr("class", ".thresh"); 
     //Add the path to draw lines and clipping so they stay within bounds  
     var path = serial.append("path") 
       .attr("clip-path", "url(#clip)") 
       .attr("class", "line") 
       .attr("d", function (d) {return line(d.values);}) 
       .style("stroke", function(d) {return color(d.key);}); 

     //Custom path to add a line showing the temperature threshold 
     var threshpath = threshElement.append("path") 
       .attr("class", "line") 
       .attr("d", function(d) { return threshold(d.values);}) 
       .style("stroke", "red"); 

     //Add a label to the end of the threshold line denoting it as the threshold 
     threshElement.append("text") 
       .attr("transform", "translate(" + x(getMaxDate(data)) + "," + y(thresholdTemp + .5) + ")") 
       .attr("x", 3) 
       .attr("dy", ".35em") 
       .text("Threshold"); 
     //Add the legend 
     plotLegend(data); 

     //Load in the new data and add it to the SVG 

     function transition() { 
       var newdata = loadMinutesJSON(minutes); 
       newdata = convertJSON(newdata); 
       console.log(newdata.length); 
       newdata = d3.nest().key(function(d) { return d.serial; }).entries(newdata); 
       if (data[0]["values"][0]["date"].toString() === newdata[0]["values"][0]["date"].toString()) { console.log("No New Data");return;} 

       //Merge the new data with the old and remove duplicates 
       data = merge(data,newdata); 
       //Chop off the preceding data that is no longer needed or visible 
       data = reduce(data, minutes); 

       x.domain([getMinDate(data), getMaxDate(data)]); 
       y.domain([45, getMaxTemp(data) + 10]); 
       d3.select(".x.axis") 
         .transition() 
         .duration(500).call(xAxis) 
         .selectAll("text") 
         .style("text-anchor", "end") 
         .attr("dx", "-.8em") 
         .attr("dy", ".15em") 
         .attr("transform" , function (d) {return "rotate(-35)"}); 

       d3.select(".y.axis").transition().duration(500).call(yAxis); 
       var serial2 = svg.selectAll(".serial") 
         .data(data, function(d) { return d.key;}); 
         serial2.enter().append("g").attr("class", "line"); 

       var threshold2 = svg.selectAll(".thresh") 
         .data(data, function(d) { return d.key;}); 
         threshold2.enter().append("g").attr("class", "line"); 

       threshpath 
         .attr("d", function(d) {return threshold(d.values);}) 
       .attr("transform", null) 
         .transition() 
         .duration(500) 
         .ease("linear"); 

       threshElement.selectAll("text") 
         .attr("transform", "translate(" + x(getMaxDate(data)) + "," + y(thresholdTemp + .5) + ")") 
         .attr("x", 3) 
         .transition() 
         .duration(500) 
         .ease("linear"); 

       path 
         .attr("d", function(d) { return line(d.values);}) 
         .attr("transform", null) 
         .transition() 
         .duration(500) 
         .ease("linear"); 
         //.attr("transform", "translate(" + x(0) + ")"); 
       console.log(svg); 
       serial2.exit().remove(); 
       threshold2.exit().remove(); 

       //Remove the old, unused data 
       //data = reduce(data, minutes); 
     } 

     if(date1 == undefined && date2 == undefined) { 
       //Set the transition loop to run every 30 seconds 
       transInterval = setInterval(transition,30000); 

     } 
} 

Вот пример структуры данных:

[{"serial":"2D0008017075F210","date":"2013-08-23T14:35:19.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"serial":"2D0008017075F210","date":"2013-08-23T14:35:50.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"serial":"2D0008017075F210","date":"2013-08-23T14:36:20.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"serial":"2D0008017075F210","date":"2013-08-23T14:36:50.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"serial":"2D0008017075F210","date":"2013-08-23T14:37:20.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"serial":"2D0008017075F210","date":"2013-08-23T14:37:50.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"serial":"2D0008017075F210","date":"2013-08-23T14:38:20.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"serial":"2D0008017075F210","date":"2013-08-23T14:38:50.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"serial":"2D0008017075F210","date":"2013-08-23T14:39:20.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"serial":"2D0008017075F210","date":"2013-08-23T14:39:50.000Z","reading":76.1,"elevation":null,"room":null,"system":null},{"serial":"1D00080170496D10","date":"2013-08-23T14:35:19.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"serial":"1D00080170496D10","date":"2013-08-23T14:35:50.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"serial":"1D00080170496D10","date":"2013-08-23T14:36:20.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"serial":"1D00080170496D10","date":"2013-08-23T14:36:50.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"serial":"1D00080170496D10","date":"2013-08-23T14:37:20.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"serial":"1D00080170496D10","date":"2013-08-23T14:37:50.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"serial":"1D00080170496D10","date":"2013-08-23T14:38:20.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"serial":"1D00080170496D10","date":"2013-08-23T14:38:50.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"serial":"1D00080170496D10","date":"2013-08-23T14:39:20.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"serial":"1D00080170496D10","date":"2013-08-23T14:39:50.000Z","reading":73.4,"elevation":null,"room":null,"system":null},{"serial":"380008017037ED10","date":"2013-08-23T14:35:19.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"serial":"380008017037ED10","date":"2013-08-23T14:35:50.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"serial":"380008017037ED10","date":"2013-08-23T14:36:20.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"serial":"380008017037ED10","date":"2013-08-23T14:36:50.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"serial":"380008017037ED10","date":"2013-08-23T14:37:20.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"serial":"380008017037ED10","date":"2013-08-23T14:37:50.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"serial":"380008017037ED10","date":"2013-08-23T14:38:20.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"serial":"380008017037ED10","date":"2013-08-23T14:38:50.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"serial":"380008017037ED10","date":"2013-08-23T14:39:20.000Z","reading":74.3,"elevation":null,"room":null,"system":null},{"serial":"380008017037ED10","date":"2013-08-23T14:39:50.000Z","reading":74.3,"elevation":null,"room":null,"system":null}] 
+1

Утечки не могут быть легко видны в коде. Я предлагаю вам запустить хронологию в Chrome и изучить использование памяти, а также то, что работает. – Joseph

+0

Прочитайте эту статью, чтобы узнать, как обнаружить утечки памяти: https://developers.google.com/chrome-developer-tools/docs/tips-and-tricks#three-snapshot-technique. –

+1

Я вижу, что вы приняли ответ, но мне любопытно, какая часть кода оказалась просачивающейся? Это было специфично для некоторых браузеров? – explunit

ответ

1

Смотреть использование памяти с помощью отладчика. Удалить код. Проверьте, не исчезла ли утечка памяти. Промыть и повторить.

Кроме того, в качестве предложения, не используйте setInterval(). Вместо этого используйте setTimeout().

+0

Звучит как строка из сообщения в блоге. Может, Османи? – Joseph

+1

@JosephtheDreamer Nope, просто прямо отлаживая шлифовку. –

+0

Я скажу, что я предпочитаю setInterval() над setTimeout в этом конкретном случае. Один, он работает только каждые 30 секунд, поэтому разница в значительной степени незначительна. Другая причина заключается в том, что setInterval() легче отменить, поэтому я могу загружать новые наборы данных всякий раз, когда захочу (это то, что я хочу сделать). – user2711335

Смежные вопросы