对javascript 线程的理解
今天和朋友讨论了一个js 线程的问题 千言不如一例 代码如下
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <script> function fun1() { setTimeout(function(){alert(1);},0) while(true) {}; } </script> <body> <div id="div1">abc</div> <input type="button" value="测试" onclick="fun1();"> </body> </html>
如果按照以前js 顺序执行的思路来理解 那么点击测试按钮后 应该会先弹出alert(1) 然后死循环 (错 了)
但是结果却是 点击按钮后直接死循环
出现這个问题的主要原因是对SetTimeout這个函数不清楚, 对javascript 单线程的理解不清楚造成的。
首先我们对浏览器中的线程了解一下
通常一个浏览器会至少存在三个线程:JS引擎线程(用于处理JS)、GUI渲染线程(用于页面渲染)、浏览器时间触发线程(用于控制交互)。
而因为JS可以操作DOM元素,进而会影响到GUI的渲染结果,因此JS引擎线程与GUI渲染线程是互斥的。也就是说当JS引擎线程处于运行状态时,GUI渲染线程将处于冻结状态。
JS引擎是基于事件驱动,采用的是单线程运行机制。即JS引擎会只会顺序的从任务列表中取任务,并执行。
SetTimeout/SetInternal
其中SetTiemout:在指定的毫秒数后调用指定的代码段;SetInternal:在指定的时间间隔内(ms)循环调用指定的代码段。这两个函数内都涉及到时间计数器,也就是都涉及到一个类似与MFC定时器。JS引擎本身就只能单线程运行,因此定时器需要由其他的外部线程来启动。所以对JS引擎而言,定时器线程可以被视为异步线程。但当定时器时间到达后,所触发的事件则必须在任务列表中排队,等候JS引擎的处理。
那么就不难理解了
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <script> function fun1() { //--------任务1-------- setTimeout(function(){alert(1);},0) //调用外部线程 后立即将代码push到js引擎的任务列表中 ---------任务2--------- while(true) {}; } //所以任务列表中的顺序应该是 //[ // 任务1,--死循环 因为js非阻塞的机制 所以导致代码不会执行到任务2 // 任务2 //] </script> <body> <div id="dom1">abc</div> <input type="button" value="测试" onclick="fun1();"> </body> </html>
好了 铺垫已经打好了 接下来进入正题 dome操作也类似
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <script> function fun1() { //--------任务1-------- document.getElementById('dom1').innerHTML = 123; //因为操作了dome 所以得调用外部线程(GUI渲染线程)JS引擎线程与GUI渲染线程互斥 所以dome操作冻结 (可以理解为添加了一个任务2在S引擎的任务列表里面么?)-------任务2-------- while(true) {}; //不需要调用外部线程 所以這个循环属于任务1 } //所以任务列表中的顺序应该是 //[ // 任务1,--死循环 因为js非阻塞的机制 所以导致代码不会执行到任务2 // 任务2 //]
//最后有点疑问 如果我用debugger去调试的话 确会改变dom1的内容为123 然后才会进入循环???? </script> <body> <div id="dom1">abc</div> <input type="button" value="测试" onclick="fun1();"> </body> </html>
setTimeout 的妙用
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <script> function fun1() { //--------任务1-------- document.getElementById('dom1').innerHTML = 123; //因为操作了dome 所以得调用外部线程(GUI渲染线程)JS引擎线程与GUI渲染线程互斥 所以dome操作冻结 (可以理解为添加了一个任务2在S引擎的任务列表里面么? 任务列表Array.push(任务2) )-------任务2-------- setTimeout(function(){while(true) {};},0); //调用外部线程 然后将触发的事件添加到任务列表中 -----任务3----- } //所以任务列表中的顺序应该是 //[ // 任务1, // 任务2, --dome操作 // 任务3, --循环 //] </script> <body> <div id="dom1">abc</div> <input type="button" value="测试" onclick="fun1();"> </body> </html>