这两天项目中遇到一个效率瓶颈,在网页中点击按钮,通过ajax读取xml文件中的数据,在得到数据并处理完成后,将得到的数组绑定到HighCharts插件中,以实现实时更新图表数据。
但问题是,这一过程首先要ajax请求xml,接着又去解析xml,而处理数组时又是一个可能长达上千次的for循环,最后才去给图表绑定新数据。这是一个相当耗时的过程,在IE中,这个过程大概要将近5到7秒,以致于IE直接抛出一个警告:“脚本执行事件过长,是否停止执行?”当选择“是”时,接下来的处理都被忽略;这不是最糟糕的,最糟糕的是,客户显然无法忍受每次点击按钮都有这样一个提示框!
经过测试,那个超长的for循环仅仅消耗了大概0.5秒的时间,而大部分时间都被图标绑定数据那几条代码给用了!因为页面中总共需要更新3个图表,所以有如下数据绑定的代码:
chart1.xAxis[0].setCategories(column1,true);
chart1.series[0].setData(column19);
chart1.series[1].setData(column35);
chart2.xAxis[0].setCategories(column1,true);
chart2.series[0].setData(column20);
chart2.series[1].setData(column36);
chart3.xAxis[0].setCategories(column1,true);
chart3.series[0].setData(column39);
chart3.series[1].setData(column40);
代码中column等参数都是处理后得到的数组,长度大概在200以内。我没有去查看HighCharts的源码,也不知道它到底干了什么,仅仅这样一个重新绑定数据的代码就能耗时数秒!
开始的时候我看着这几行代码,感觉还无头绪,我实在想不出还能怎样优化这几行代码。一开始我的思路是有没有其它比较高效的数据绑定方式,但很快就被否定了。看上去这几行代码都已经无法精简了,充其量还能来一个一次性为两个series赋值,但这解决不了效率问题。
否决了这个思路后,我只能从另一个角度其解决问题了。我想到,是不是可以将这几行代码分开执行,最好是它们互不知道对方的存在。这类似于多线程,而js中实现多线程的方法无意就是setTimeout或者setInterval这两个函数。显然,仅需要执行一次的代码,应该使用setTimeout。
我将这三个图表的更新放在了三个setTimeout中执行,于是得到了如下代码:
setTimeout(function(){
chart1.xAxis[0].setCategories(column1,true);
chart1.series[0].setData(column19);
chart1.series[1].setData(column35);
},1);
setTimeout(function(){
chart2.xAxis[0].setCategories(column1,true);
chart2.series[0].setData(column20);
chart2.series[1].setData(column36);
},1);
setTimeout(function(){
chart3.xAxis[0].setCategories(column1,true);
chart3.series[0].setData(column39);
chart3.series[1].setData(column40);
},1);
这样的处理并不影响最后的结果,只是将三部分代码分别放在三个setTimeout中,以达到让它们全都在1毫秒后再执行;而1毫秒后,三者的执行是不存在顺序关系的,也就是说,三者互不阻塞。这样的话,就不会因为三者积累的效率问题而弹出一个令人厌恶的对话框了。
虽然这样的处理带来的效率并不明显,但至少欺骗了浏览器,让浏览器认为,这是一段执行效率还行的代码!