javascript的执行引擎是单线程的,正常状况下是同步编程的模式,便是程序按照代码的顺序从上到下依次顺序执行。只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),每每就是由于某一段Javascript代码长时间运行(好比死循环),那么在执行期间任何UI更新都会被阻塞,界面事件处理也会中止响应。致使整个页面卡在这个地方,其余任务没法执行。javascript
特别是在for循环语句里,若是for循环的处理逻辑比较复杂,而且循环次数过多,超过1000次时,javascript的执行会阻塞浏览器处理起来会有明显的假死状态。缘由就是浏览器在调用javascript的时候,主界面是中止响应的,由于cpu交给js执行了,没有时间去处理界面消息。html
为了解决卡死的问题,不少人提出了异步编程的解决方案,这也是性能优化的其中一个方式,在浏览器端,耗时很长的操做都应该异步执行,避免浏览器失去响应。如今很火的nodejs就是异步编程,好比路由派发,IO操做,都是异步的。前端
在前端页面实现中,最多见的异步就是ajax操做,请求一个ajax无需等待ajax返回,则可继续操做页面。java
其余的还有经过setTimeout
,setInterval
,image.onload
, postMessage,webwork
等方式进行异步编程实现。node
网上也有不少库实现了异步编程如:do.js
. step.js
, async.js
, flow.js
,就不详细阐述了,有兴趣的自行google了解。jquery
这里主要讲setTimeOut实现异步编程的方式。web
先看一段代码,http://jsfiddle.net/jd_felix/mmrL66na/1/ajax
<!DOCTYPE html> <html> <head> <title>DEMO</title> </head> <script src="http://codeorigin.jquery.com/jquery-1.10.2.min.js"></script> <script> var updateSync = function() { for (var i = 0; i < 1000; i++) { $('#js_output').text(i); } } var updateAsync = function() { var i = 0; function updateLater() { $('#js_output').text(i++); if (i < 1000) { setTimeout(updateLater, 0); } } updateLater(); } </script> <body> <button onclick="updateSync()">同步DEMO</button> <button onclick="updateAsync()">异步DEMO</button> <div id="js_output"></div> </body> </html>
点击同步DEMO:你会感受按钮按下去的时候卡了一下,而后看到一个最终结果99999,而没有中间过程,这就是由于在updateSync函数运行过程当中UI更新被阻塞,只有当它结束退出后才会更新UI。编程
点击异步DEMO:你会看到UI界面上从0到999快速地更新过程,这就是异步执行的结果。 函数里先声明了一个局部变量i和嵌套函数updateLater,而后调用了updateLater,在这个函数中先是更新output结点的内容为i, 而后经过setTimeout让updateLater函数异步执行。这实际是一种递归调用。任何for循环均可以改形成递归调用的形式。promise
这是由于虽然他的delay设置为0,几乎是即时触发,但仍是被添加到了执行队列后面,但就是这个过程,渲染已经完成了,当他回调函数执行时,输出来的已是更新后的值了。
以上结果很显然,异步操做不会阻塞UI,你能够继续执行浏览器其余操做。让UI操做更流畅,但异步编程也有坏处,如上面代码,使用setTimeout
的异步方式,在代码总体执行效率来看,要比同步执行耗时更长时间。同时因为是异步执行,打断了原有代码的执行顺序,形成嵌套的函数调用,破坏了原有的简单逻辑,让代码难以读懂。
在判断是否执行完毕时,在同步编程中很方便实现,代码写在for循环后面就好了。而异步的话,则须要作一些判断。
仍是以上的例子,如何在循环结束后执行回调?可使用Jquery
的$.when
,和$.Deferred
方法,固然也能够本身写回调函数,可是看起来没那么优雅。
var wait = function(){ var dtd = $.Deferred(); var i = 0; function updateLater() { $('#js_output').text(i++); if (i < 1000) { setTimeout(updateLater, 0); } if(i == 1000){ dtd.resolve(); // 改变Deferred对象的执行状态 } } updateLater(); return dtd.promise(); // 返回promise对象 } var updateAsyncBack = function(){ $.when(wait()).done(function(){ alert('done!'); }) }
原文请看:http://faso.me/notes/20131123/javascript-synchronous-settimeout/