本篇学习笔记内容源于《Secrets of the JavaScript Ninja》
众所周知,JS在浏览器中是以单线程的方式运行的,因此,JS在异步处理问题时,采用的是排队等候的方式,即当某个JS事件触发时,如果队列里没有其他要执行的JS代码,则该事件立即执行,否则,按照先进先出的方式进入队列等候。
备注:setInterval()方法具有定义一个按照指定间隔时间连续触发某函数的功能,但在浏览器的JS队列里,只能存储唯一的一个有某个setInterval()方法所产生的触发事件,即如果setInterval()每隔3秒触发执行一个函数,但此时浏览器在5秒钟之内一直被某个应用占用者,那么setInterval()所触发的事件只能有一个进入队列,当第二个进入队列时会被丢弃,如果第一个进入队列的事件执行完毕后,才能再进入下一个。因此setInterval()所触发的事件可能会出现“丢帧”的现象。
备注可以先不管它,我们接第一段的内容。当浏览器中处理某个复杂运算时,会占用浏览器较长的时间,由于JS的单线程特性,此时如果用户在浏览器上进行一些交互操作,很可能出现“卡”的现象,如果运气不佳,那浏览器会“死”在那里(一些浏览器会监控单线程的执行情况,如果在几秒内一直有某个运算占据着线程不放,浏览器会及时终止正在执行的线程,以腾出资源让队列里的其他事件得以触发执行)。
但有时因为特殊需求,我们需要在浏览器中执行一些复杂操作,例如在一组数量较大的DOM树中进行一些操作。为了避免上述可能出现的浏览器停滞问题,我们需要想一些办法。此时定时器timers也许能帮上忙,是的,它能将一段需要长时间执行的程序,分解成若干小段,并以某一间隔时间依次触发小段程序,最终完成复杂的操作。
<div> <table><tbody></tbody></table> </div> <script type="text/javascript"> var tbody = document.getElementsByTagName("tbody")[0]; for (var i = 0; i < 20000; i++) { var tr = document.createElement("tr"); for (var t = 0; t < 6; t++) { var td = document.createElement("td"); td.appendChild(document.createTextNode(i + "," + t)); tr.appendChild(td); } tbody.appendChild(tr); } </script>
上面代码中的主要操作会循环执行2000*6=120000次,浏览器至少也得执行5秒钟以上,我的点好执行了大约10秒呵呵,期间什么也干不了,只能等待,这种体验糟透了,所以需要利用timer进行改进,代码如下:
<script type="text/javascript"> var rowCount = 20000; var divideInto = 10; var chunkSize = rowCount/divideInto; var iteration = 0; var table = document.getElementsByTagName("tbody")[0]; setTimeout(function generateRows(){ var base = (chunkSize) * iteration; for (var i = 0; i < chunkSize; i++) { var tr = document.createElement("tr"); for (var t = 0; t < 6; t++) { var td = document.createElement("td"); td.appendChild( document.createTextNode((i + base) + "," + t + "," + iteration)); tr.appendChild(td); } table.appendChild(tr); } iteration++; if (iteration < divideInto) setTimeout(generateRows, 50); }, 0); </script>
上面的代码并不难懂,在开始设置了一些变量,用来把大循环分解成的若干个(10个)小循环,每次小循环执行后,再间隔50ms调用generateRows()函数,这样浏览器虽然反应迟钝,但还在不会死掉了。