今天和朋友讨论了一个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
S引擎本身就只能单线程运行,因此定时器需要由其他的外部线程来启动。所以对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>