一.同步和异步的概念。
同步:即按代码的顺序执行任务。
在下列代码中,按照同步概念,则是先打印1后打印2。
1 console.log(1); 2 console.log(2);
异步:即执行一个任务的同时执行另一个任务。如果按照此概念执行上面代码,则是同时打印出1和2。
二.客户端java script中代码的执行顺序
首先,不管是核心java script还是客户端java script都不包含任何线程机制,只有一个单线程执行模型。单线程即指脚本和事件处理程序在同一时间只能执行一个,不能同时执行,没有并发性。(HTML5定义了一种为后台线程的“Web Worker”,本人不甚了解,不做赘述)。
单线程的好处在于编程更加简单,编写代码可以确保两个事件处理程序不会同时运行,操作DOM文档也不会担心有其他线程同时修改文档。但这也意味着java script脚本和事件处理程序不能运行太久,否则会降低网页的可读性,甚至导致浏览器奔溃的假象。那么,一个JS程序是怎么具体执行的呢?
JS的执行任务分为同步任务和异步任务:
同步任务:指除了异步任务之外的其他程序。
异步任务:指各种事件(比如资源载入事件中的loaded、DOMContentLoaded中的回调函数,普通事件中的click,focus,mouseover中的回调函数,window对象的定时器setInterval、setTimeout中的回调函数等等)。
过程:一个js程序执行时,首先将同步任务放入一个执行栈中,先解析同步任务和异步任务并且按顺序执行所有同步任务。当异步任务被触发时(如用户点击鼠标或者按下键盘),异步进程处理则会检测到并将其对应的异步任务转移到任务队列中。同步任务全部执行完毕后,则查看任务队列中是否有未完成的回调函数,如果有则按顺序执行。在此后期间会不断查看任务队列并不断执行,形成事件循环。请看如下过程图
三、HTML文件中script标签的执行顺序和其属性defer、async产生的影响
1.在默认情况下,HTML解析器遇到script标签时,是先执行脚本,进入脚本并按上面所述的顺序执行完代码。然后再继续解析渲染HTML页面文档,这是对于内联脚本来说。但同样的,对于一个由src属性指定外部文件的脚本来说,也是先下载并执行该脚本。也就是说,在完成改脚本的下载和执行前,其后面的文档部分都不会显现出来(实际上DOM树已经被载入,但是没被解析为DOM树)。
以下一个1996最先进的JS代码可以证明该概念(当时没有那么多的异步事件API实现异步调用,所以用如下的同步程序来实现动态添加HTML元素)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 </head> 7 <body> 8 <h1>Table of Factorials</h1> 9 <script> 10 function factorial(n) { //用来实现阶乘的函数 11 if(n <= 1) return n; 12 else return n * factorial(n-1); 13 } 14 15 document.write("<table>"); //开始创建表格 16 document.write("<tr><th>n</th><th>n!</th></tr>"); //创建表头 17 for(var i = 0;i <= 10;i++) { 18 document.write("<tr><td>" + i + "</td><td>" + factorial(i) + "</td></tr>"); //输出十行表格 19 } 20 document.write("</table>"); //表格结束 21 document.write("Generated at " + new Date()); //输出时间戳 22 </script>
<h2>Table of Factorials</h2> 23 </body> 24 </html>
以下为结果图:
可以得知,脚本的执行在默认的情况下是同步和阻塞的,这是由其单线程模型决定的。但是,对于使用src引入外部文件的script标签来说,其属性defer和async可以改变这种情况,实现异步调用
2.对于外联脚本(即由src属性引入外部js文件的脚本),其有两个属性可以改变同步状态——defer和async(只要有这两个属性之一即为异步脚本)
defer(延迟):有了该属性的外联脚本会延迟解析执行,即等待文档的载入和解析完成并可以操作时(不包括img,即可以理解为DOMContentLoaded事件触发时)才解析执行。看以下代码:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 <style type="text/css" media="screen"> 7 div { 8 width: 100px; 9 height:100px; 10 } 11 </style> 12 </head> 13 <body> 14 <script type="text/java script" src="console.js" defer></script> 15 <script type="text/java script"> 16 console.log(+new Date()); 17 </script> 18 <script type="text/java script"> 19 document.addEventListener("DOMContentLoaded",function(){ 20 console.log(+new Date()); 21 }); 22 </script> 23 </body> 24 </html>
console.js的代码