js大量数据计算导致页面假死

JavaScript014

js大量数据计算导致页面假死,第1张

为什么页面会卡顿呢,以60Hz为例,即一秒钟的动画就是由60张静态图片连在一起。60fps是动画播放比较理想、比较基础的要求,windows系统有个刷新频率也是这个意思。60fps就要求一帧的时间为1s/60=16.67ms。浏览器显示页面的时候,要处理js逻辑,还要做渲染,每个执行片段的时间不能超过16.67ms。实际上,浏览器内核自身支撑体系运行也需要消耗一些时间,所以留给我们的时间差不多只有10ms。并且在处理js计算时,浏览器不会响应用户的操作,所以就造成了页面“假死”。 Web Work,就是为JavaScript创造多线程环境,允许主线程创建Web Worker线程,将一些任务分配给后台运行。在主线程运行的同事,Work线程在后台运行,两者互不干扰。等到Work线程完成计算任务再把结果返回给主线程。这样的好处是,一些密集或者高延迟的计算任务,被Work线程给分担了,这样主线程就会很流程。 Worker线程一旦创建成功,就会始终运行,不会被主线程上的活动打断取消。这样有利于随时响应主线程的通信。但是,这也造成了Worker比较耗费资源,不应该过度使用,所以一旦使用完毕,就应该关闭。 1.同源限制:分配给Worker线程运行的脚本文件,必须与主线程的脚本文件同源。 2.DOM限制:Work线程所在的全局对象和主线程不一样,所以无法读取主线程所在网页的DOM对象,也无法使用document,window,parent这些对象。但是可以使用navigator和location。 3.通信联系:Worker线程和主线程不在同一个上下文环境,他们不能直接通信,必须通过消息完成。 4.脚本限制:Worker线程不能执行alert和confirm方法,但是可以使用XMLHttpRequest对象发出的AJAX请求。 5.文件限制:Work线程不能读取本地文件,它所加载的脚本必须来自网络。

1:JS中大量的DOM操作也会导致事件响应缓慢甚至真正卡死浏览器,如在IE6下一次插入大量的HTML。而如果真的弹出了“脚本运行时间过长“的提示框则说明你的JS脚本肯定有死循环或者进行过深的递归操作了。

2:这时候需要对脚本进行优化,其步骤如下:

第一步,优化循环,循环体中包含太多的操作和循环的次数过多都会导致循环执行时间过长,并直接导致锁死浏览器。如果循环之后没有其他操作,每次循环只处理一个数值,而且不依赖于上一次循环的结果则可以对循环进行拆解,看下面的chunk的函数:

function chunk(array, process, context){

setTimeout(function(){

var item = array.shift()

process.call(context, item)

if(array.length >0){

setTimeout(arguments.callee,100)

}),100)

}

chunk()函数的用途就是将一个数组分成小块处理,它接受三个参数:要处理的数组,处理函数以及可选的上下文环境。每次函数都会将数组中第一个对象取出交给process函数处理,如果数组中还有对象没有被处理则启动下一个timer,直到数组处理完。这样可保证脚本不会长时间占用处理机,使浏览器出一个高响应的流畅状态。

其实看来,借助JS强大的闭包机制任何循环都是可拆分的,下面的版本增加了callback机制,使可再循环处理完毕之后进行其他的操作。

function chunk(array,process,cbfun){

var i=0,len = array.length //这里要注意在执行过程中数组最好是不变的

setTimeout(function(){

process( array[i], i++) //循环体要做的操作

if( i <len ){

setTimeout(arguments.callee,100)

}else{

cbfun()//循环结束之后要做的操作

}

}

}

第二步,优化函数,如果函数体内有太多不相干但又要一起执行的操作则可以进行拆分,考虑下面的函数:

function dosomething(){

dosomething1()

dosomething2()

}

dosomething1和dosomething2互不相干,执行没有先后次序,可用前面提到的chunk函数进行拆分:

function dosomething(){

chunk([dosomething1,dosomething2],function(item){item()})

}

或者直接交给浏览器去调度

function dosome(){

setTimeout(dosomething1,0)

setTimeout(dosomething2,0)

}

第三步,优化递归操作,函数递归虽然简单直接但是过深的递归操作不但影响性能而且稍不注意就会导致浏览器弹出脚本失控对话框,必须小心处理。

看以下斐波那契数列的递归算法:

function fibonacci(n){

return n <2? n: fibonacci(n -1)+ fibonacci(n -2)

}

fibonacci(40)这条语句将重复调用自身331160280次,在浏览器中执行必然导致脚本失控,而采用下面的算法则只需要调用40次

fibonacci =function(n){

var memo ={0:0,1:0}//计算结果缓存

var shell =function(n){

var result = memo[n]

if(typeof result !='number')//如果值没有被计算则进行计算

memo[n]= shell(n-1)+ shell(n -2)

return memo[n]

}

return shell(n)

}

这项技术被称为memoization,他的原理很简单就是同样的结果没必要计算两次。另一种消除递归的办法就是利用迭代,递归和迭代经常会被作为互相弥补的方法。

第四步,减少DOM操作,DOM操作的代价是相当昂贵的,大多数DOM操作都会触发浏览器的回流(reflow)操作。例如添加删除节点,修改元素样式,获取需要经过计算的元素样式等。我们要做的就是尽量少的触发回流操作。

el.style.width ='300px' el.style.height ='300px' el.style.backgroundColor ='red'

上面的操作会触发浏览器的三次回流操作,再看下面的方式:

el.className ='newStyle'

通过设置改元素的className一次设置多个样式属性,将样式写再CSS文件中,只触发一次回流,达到了同样是效果而且效率更高。因为浏览器最擅长的就是根据class设置样式。

还有很多可以减少DOM操作的方法,在此就不多说了,但是一个基本的原则就是让浏览器去做它自己擅长的事情,例如通过class来改变元素的属性。

相信经过上面的优化的过程必定可以大大提高用户体验,不会出现浏览器被锁死和弹出脚本失控的对话框,使你的浏览器从繁重的任务中解放出来。需要指出的是上面这些优化并不是必须的,只有当一段脚本的执行时间真的影响到了用户体验才需要进行。虽然让用户觉得脚本的执行变快了,但其实完成同一个操作的时间可能被延长了,这些技术只是让浏览器处于一个快速响应的状态,使浏览更流畅。

载入脚本或者脚本在执行了一些较多的页面载入完成的事件 可能由于js文件未完全载入导致假死

刷新之后可能文件载入完成操作就正常了

泽元的框架太大了 压缩之后还100多k

建议使用我的框架http://hi.baidu.com/zkunet/blog/item/01cf700f1d3f49c17acbe1a2.html

第二关于弹出层的问题 我也自己写了一个(当然参考了泽元的代码 修正泽元的bug 具体bug就不指出了) 当然ui是用的泽元的 我发给你代码

图片相信你自己有就不用我给你了吧

注意:上面的框架不要和下面的弹出层放一起 我没有做检查 有些函数可能在我的框架里已经有了 可能导致冲突报错 等以后检查了 再到我的空间来看

/*xs.Dialog.js 作者:肖毅 转载请保留这段申明 http://hi.baidu.com/zkunet/ */

var PicPath = "/MX_Admin/Images/"

var isIE = navigator.userAgent.toLowerCase().indexOf("msie") != -1

var isGecko = navigator.userAgent.toLowerCase().indexOf("gecko") != -1

//获取当前窗口最顶级的window对象

function $TW() {

var pw = window

while(pw!=pw.parent){

pw = pw.parent

}

return pw

}

//阻止一切事件执行,包括浏览器默认的事件

function stopEvent(event){

if(!event){

return

}

if(isGecko){

event.preventDefault()

event.stopPropagation()

}

event.cancelBubble = true

event.returnValue = false

}

function Dialog(sID) {

this.ID = sID

this.Width = 400

this.Height = 200

this.Title = "系统提示-网页对话框"

this.URL = null

this.Alert = ""

this.DialogArguments = {}

this.Message = null

this.MessageTitle = null

this.ShowMessageRow = false

this.ShowButtonRow = true

this.Icon = null

this.Mask=null

}

Dialog._Array = []

Dialog.prototype.show = function(){

var pw = $TW()

var mw = Math.max(pw.document.body.clientWidth,pw.document.body.scrollWidth)

var mh = Math.max(pw.document.body.clientHeight,pw.document.body.scrollHeight)

this.DialogArguments._DialogInstance = this

this.DialogArguments.ID = this.ID

//UI创建

var arr = []

arr.push("<table style='-moz-user-select:none' oncontextmenu='stopEvent(event)' onselectstart='stopEvent(event)' width='100%' height='100%' border='0' cellspacing='0' cellpadding='0'>")

arr.push("<tr id='_DialogTitle_"+this.ID+"' style='backgound-color:'>")

arr.push("<td width='13' height='33' align='center' style='background:url("+PicPath+"dialog_title.png)background-repeat:no-repeat'>")

arr.push("<div style='width:13px'></div></td>")

arr.push("<td height='33' style='background:url("+PicPath+"dialog_ct.png)background-repeat:repeat-x'>")

arr.push("<div style='float:leftfont-weight:boldcolor:#FFFFFFpadding:9px 0 0 4pxfont-size:12px'>")

arr.push("<img src='"+PicPath+"icon_dialog.gif' align='absmiddle'> "+this.Title+"</div>")

arr.push("<div style='position: relativecursor:pointerfloat:rightmargin:5px 0 0_margin:4px 0 0height:17pxwidth:28pxbackground:url("+PicPath+"dialog_closebtn.gif)' onMouseOver=\"this.style.backgroundImage='url("+PicPath+"dialog_closebtn_over.gif)'\" onMouseOut=\"this.style.backgroundImage='url("+PicPath+"dialog_closebtn.gif)'\" id='_DialogClose_"+this.ID+"' drag='false' onClick=\"Dialog.getInstance('"+this.ID+"').CancelButton.onclick.apply(Dialog.getInstance('"+this.ID+"').CancelButton,[])\"></div></td>")

arr.push("<td width='13' height='33' style='background:url("+PicPath+"dialog_rt.png)background-repeat:no-repeat'>")

arr.push("<div style='width:13px'></div></td></tr>")

arr.push("<tr drag='false'><td width='13' style='background:url("+PicPath+"dialog_mlm.png)background-repeat:no-repeat'></td>")

arr.push("<td align='center' valign='top'>")

arr.push("<table width='100%' height='100%' border='0' cellpadding='0' cellspacing='0' bgcolor='#FFFFFF'>")

arr.push("<tr id='_MessageRow_"+this.ID+"' style='display:none'>")

arr.push("<td height='50' valign='top'><table id='_MessageTable_"+this.ID+"' width='100%' border='0' cellspacing='0' cellpadding='8' style='background:#EAECE9 url("+PicPath+"dialog_bg.jpg) no-repeat right top'>")

arr.push("<tr><td width='40' height='50' align='right'><img id='_MessageIcon_"+this.ID+"' src='"+PicPath+"window.gif' width='32' height='32'></td>")

arr.push("<td align='left' style='line-height:16pxpadding-left:6px'>")

arr.push("<h5 id='_MessageTitle_"+this.ID+"'> </h5>")

arr.push("<div id='_Message_"+this.ID+"'> </div></td>")

arr.push("</tr></table></td></tr>")

arr.push("<tr><td align='center' valign='top'")

arr.push("valign='top'>")

arr.push("<iframe src='"+this.URL+"' id='_DialogFrame_"+this.ID+"' allowTransparency='true' width='100%' height='100%' frameborder='0' style='background-color: #transparentborder:noneoverflow-x:hidden'></iframe>")

arr.push("</td></tr>")

arr.push("<tr drag='false' id='_ButtonRow_"+this.ID+"'><td height='36'>")

arr.push("<div id='_DialogButtons_"+this.ID+"' style='text-align:rightborder-top:#dadee5 1px solidpadding:8px 20pxbackground-color:#f6f6f6'>")

arr.push("<input id='_ButtonOK_"+this.ID+"' type='button' value='确 定'>")

arr.push("<input id='_ButtonCancel_"+this.ID+"' type='button' onclick=\"Dialog.getInstance('"+this.ID+"').close()\" value='取 消'>")

arr.push("</div></td></tr>")

arr.push("</table></td><td width='13' style='background:url("+PicPath+"dialog_mrm.png)background-repeat:no-repeat'></td></tr>")

arr.push("<tr><td width='13' height='13' style='background:url("+PicPath+"dialog_lb.png)background-repeat:no-repeat'></td>")

arr.push("<td style='background-image:url("+PicPath+"dialog_cb.png)background-repeat:repeat-x'></td>")

arr.push("<td width='13' height='13' style='background-image:url("+PicPath+"dialog_rb.png)background-repeat:no-repeat'></td>")

arr.push("</tr></table>")

var Mask = pw.$("_DialogMask")

if(!Mask) {

Mask = pw.document.createElement("div")

Mask.id = "_DialogMask"

Mask.style.position = "absolute"

Mask.style.zIndex = "5000"

Mask.style.width = mw

Mask.style.height = mh

Mask.style.top = "0px"

Mask.style.left = "0px"

Mask.style.background = "#33393C"

Mask.style.filter = "alpha(opacity=10)"

Mask.style.opacity = "0.10"

Mask.style.display = "none"

pw.document.body.appendChild(Mask)

}

//创建弹出窗口

var div = pw.document.createElement("div")

div.id = "_Dialog_"+this.ID

div.style.position = "absolute"

div.style.background=""//必须设置为空 防止在某些主题下产生白色边框

div.style.zIndex = "10000"

div.style.width = this.Width + "px"

div.style.height = this.Height + "px"

div.style.top = parseInt(pw.document.body.scrollTop+pw.document.body.clientHeight/2 - this.Height/2) + "px"

div.style.left = parseInt(pw.document.body.clientWidth/2 - this.Width/2) + "px"

div.style.display = "none"

pw.document.body.appendChild(div)

this.DialogDiv = div

div.innerHTML = arr.join('\n')

pw.$("_DialogFrame_"+this.ID).DialogInstance = this

pw.Drag.init(pw.$("_DialogTitle_"+this.ID),pw.$("_Dialog_"+this.ID))//注册拖拽方法

this.OKButton = pw.$("_ButtonOK_"+this.ID)

this.CancelButton = pw.$("_ButtonCancel_"+this.ID)

//显示标题图片

if(this.ShowMessageRow){

pw.$("_MessageRow_"+this.ID).style.display = ""

if(this.MessageTitle){

pw.$("_MessageTitle_"+this.ID).innerHTML = this.MessageTitle

}

if(this.Message){

pw.$("_Message_"+this.ID).innerHTML = this.Message

}

}

//显示按钮栏

if(!this.ShowButtonRow){

pw.$("_ButtonRow_"+this.ID).style.display = "none"

}

if(this.CancelEvent){

this.CancelButton.onclick = this.CancelEvent

}

if(this.OKEvent){

this.OKButton.onclick = this.OKEvent

this.returnValue = true

}

if(this.URL == "") {

var win = pw.$("_DialogFrame_"+this.ID).contentWindow

var doc = win.document

doc.open()

doc.write("<body oncontextmenu='return false'></body>")

var arr = []

arr.push("<table height='100%' border='0' align='left' cellpadding='10' cellspacing='0'>")

arr.push("<tr><td align='right'><img id='Icon' src='"+this.Icon+"' width='34' height='34' align='absmiddle'></td>")

arr.push("<td align='left' id='Message' style='font-size:9pt'>"+this.Alert+"</td></tr></table>")

var div = doc.createElement("div")

div.innerHTML = arr.join('')

doc.body.appendChild(div)

doc.close()

}

this.Mask = "_DialogMask"

pw.$("_Dialog_"+this.ID).style.display = ""

if(pw.$("_DialogMask").style.display == "none") {

pw.$("_DialogMask").style.display = ""

var k = 1,timer=null

timer=setInterval(function(){

k = k + 1

if(k<5) {

Mask.style.filter = "alpha(opacity="+k*10+")"

Mask.style.opacity = ""+k/10+""

}

},10)

} else {

clearInterval(timer)

}

pw.$("_Dialog_"+this.ID).focus()

pw.$("_DialogFrame_"+this.ID).focus()

if(this.ShowButtonRow){

this.OKButton.focus()

}

//放入队列中

pw.Dialog._Array.push(this.ID)

if(pw.Dialog._Array.length>1) {

for(i=0i<pw.Dialog._Array.length-1i++) {

pw.Dialog.getInstance(pw.Dialog._Array[i]).DialogDiv.style.zIndex=4000+i

}

}

}

Dialog.prototype.close = function(){

var pw = $TW()

pw.Dialog._Array.remove(this.ID)

if(pw.Dialog._Array.length==0) {

pw.$("_DialogMask").style.display = "none"

pw.document.body.removeChild(pw.$("_DialogMask"))

} else {

pw.Dialog.getInstance(pw.Dialog._Array[pw.Dialog._Array.length-1]).DialogDiv.style.zIndex=10000

pw.Drag.init(pw.$("_DialogTitle_"+pw.Dialog._Array[pw.Dialog._Array.length-1]),pw.$("_Dialog_"+pw.Dialog._Array[pw.Dialog._Array.length-1]))

}

pw.$("_Dialog_"+this.ID).style.display = "none"

pw.document.body.removeChild(pw.$("_Dialog_"+this.ID))

}

Dialog.blink = function () {

var pw = $TW()

if(pw.Dialog._Array.length>0) {

var target = pw.Dialog.getInstance(pw.Dialog._Array[pw.Dialog._Array.length-1])

var sl = parseInt(target.DialogDiv.style.left)

var st = parseInt(target.DialogDiv.style.top)

var sr = sl + target.Width

var sb = st + target.Height

if(pw.event.clientX<sl || pw.event.clientY<st || pw.event.clientX>sr || pw.event.clientY>sb) {

pw.$("_DialogTitle_"+target.ID).childNodes[1].style.filter = "alpha(opacity=20)"

pw.$("_DialogTitle_"+target.ID).style.opacity = "0.20"

setTimeout(function() {

pw.$("_DialogTitle_"+target.ID).childNodes[1].style.filter = ""

pw.$("_DialogTitle_"+target.ID).style.opacity = "1.00"

}

,50)

}

} else {

return

}

}

Dialog.getInstance = function(id){

var pw = $TW()

var f = pw.$("_DialogFrame_"+id)

if(!f){

return null

}

return f.DialogInstance

}

Dialog.Alert = function (str) {

var pw = $TW()

var diag = new Dialog("Alert")

diag.Width = 400

diag.Height = 224

diag.Title = "系统提示-网页对话框"

diag.URL = ""

diag.Alert = str

diag.Icon = PicPath+"icon_alert.gif"

diag.ShowMessageRow = false

diag.show()

Dialog.getInstance("Alert").CancelButton.value = "确 定"

Dialog.getInstance("Alert").OKButton.style.display = "none"

Dialog.getInstance("Alert").CancelButton.focus()

}

Dialog.Confirm = function (str,func1,func2) {

var pw = $TW()

var diag = new Dialog("Confirm")

diag.Width = 400

diag.Height = 224

diag.Title = "系统提示-网页对话框"

diag.URL = ""

diag.Alert = str

diag.Icon = PicPath+"icon_query.gif"

diag.ShowMessageRow = false

diag.CancelEvent = function(){

diag.close()

if(func2) {

func2()

}

}

diag.OKEvent = function(){

diag.close()

if(func1) {

func1()

}

}

diag.show()

}

Dialog.setPosition = function() {

var pw = $TW()

var DialogArr=pw.Dialog._Array

if(DialogArr==null||DialogArr.length==0)return

for(i=0i<DialogArr.lengthi++){

pw.$("_DialogFrame_"+DialogArr[i]).DialogInstance.setPosition()

}

}

Dialog.prototype.setPosition = function() {

var pw = $TW()

var mw = Math.max(pw.document.body.clientWidth,pw.document.body.scrollWidth)

var mh = Math.max(pw.document.body.clientHeight,pw.document.body.scrollHeight)

this.DialogDiv.style.top = parseInt(pw.document.body.scrollTop+pw.document.body.clientHeight/2 - this.Height/2) + "px"

this.DialogDiv.style.left = parseInt(pw.document.body.clientWidth/2 - this.Width/2) + "px"

pw.$(this.Mask).style.width= mw + "px"

pw.$(this.Mask).style.height= mh + "px"

}

Dialog.onKeyUp = function(event){

var pw = $TW()

if(window.event.keyCode==9){

if(pw.Dialog._Array.length>0){

stopEvent(window.event)

}

}

if(window.event.keyCode==27){

if(pw.Dialog._Array.length>0){

var diag = pw.Dialog.getInstance(pw.Dialog._Array[pw.Dialog._Array.length-1])

diag.CancelButton.onclick.apply(diag.CancelButton,[])

}

}

if(window.event.ctrlKey &&window.event.keyCode == 65) {

stopEvent(pw.event)

}

}

var Drag = {

target:null,

init:function(handle,dragMain) {

Drag.target = handle

Drag.target.style.cursor="move"

handle.root = dragMain

handle.onmousedown = Drag.start

},

start:function() {

var handle = Drag.target

Drag.mouseX = window.event.clientX

Drag.mouseY = window.event.clientY

Drag.flag = true

document.onmousemove = Drag.move

document.onmouseup = Drag.end

},

move:function() {

if(Drag.flag) {

if(window.event.button==1 | window.event.which==1) {

var handle = Drag.target

var mouseX = window.event.clientX

var mouseY = window.event.clientY

var top = parseInt(handle.root.style.top)

var left = parseInt(handle.root.style.left)

var cl = left + (mouseX - Drag.mouseX)

var ct = top + (mouseY - Drag.mouseY)

handle.root.style.left = cl + "px"

handle.root.style.top = ct + "px"

Drag.mouseX = mouseX

Drag.mouseY = mouseY

}

}

},

end:function() {

document.onmousemove = null

document.onmouseup = null

Drag.flag = false

}

}

if(isIE){

document.attachEvent("onkeydown",Dialog.onKeyUp)

$TW().document.attachEvent("onclick",Dialog.blink)

window.attachEvent('onresize',Dialog.setPosition)

}else{

document.addEventListener("keydown",Dialog.onKeyUp,false)

$TW().document.addEventListener("click",Dialog.blink,false)

window.addEventListener('resize',Dialog.setPosition,false)

}

function alert(str) {

Dialog.Alert(str)

}

function confirm(str,func1,func2) {

Dialog.Confirm(str,func1,func2)

}