文章目录
javascript是单线程的
javascript可以操作DOM,所以javascript是单线程的
。
为啥?
试想下,如果javascript是多线程的,就比如现在有两个线程,其中一个是给DOM节点添加内容,另一个是删除这个DOM节点,这时浏览器应该听谁的。为了避免这类复杂的同步问题,javascript就被设计成单线程
的了。
那Web Worker
怎么说?
虽然允许使用Web Worker
创建子线程,但子线程完全受控于主线程,且子线程不能操作DOM,所以Web Worker
并没有改变javascript是单线程的
这一本质。
浏览器内核是多线程的
javascript是单线程的
就意味着, 只有等待上一个任务执行完,下一个任务才会开始执行。
就像医院排队候诊一样,如果因为病情复杂需要医生花费更多的时间去分析诊断比如,任务计算量大,不得不让大家多等会儿,可以理解。但是,轮到一位女士时,她却说“哎呀呀,我得回家取趟病例先,再等我two hours”。让大家空等?当然不会,医生会让这位“麻烦女士”先回家取病例,等她取回病例了,她必须在“等候区”等待,直到医生将之前排队的病人全部诊断完毕,空闲下来了,才轮到这位“麻烦女士”。
这个"等候区"就是任务队列
。任务队列
里有什么?那我们就得了解下浏览器内核里有啥线程。
浏览器内核里的线程
定时器线程
setTimeout
和setInterval
不是由js引擎计时,由浏览器内核的Timer模块处理。
setTimeout
和setInterval
所在的线程就是定时器线程。
当计时完成,回调函数就会进入任务队列
。
事件触发线程
onclick
、onmouseover
等这些事件也不是js引擎的,由浏览器内核的DOM binding模块处理。
当事件触发时,事件处理程序会进入任务队列
。
异步http请求线程
Ajax也不是js引擎的,由浏览器内核的Network模块处理。网络请求返回后,对应的回调函数也会进入任务队列
。
javascript引擎线程
javascript引擎线程
就是javascript线程
、主线程
,用来执行javascript代码。
主线程
空闲后,才会执行任务队列
中的任务。
GUI线程
负责渲染页面,包括解析HTML、CSS,构建DOM树、Render树,布局、绘制。
页面重绘或回流,就会运行这个线程。
你肯定听说过,"script
标签的加载和解析会阻塞DOM渲染"或者遇到过类似下面的问题。
“script
标签的加载和解析”,就是执行js代码,即运行javascript引擎线程
;
“DOM渲染”,就是运行GUI线程
。
是的,GUI线程
与javascript引擎线程
是彼此互斥
的,有你没我,有我没你。
javascript引擎线程
运行时,GUI线程
是冻结的,只有javascript引擎线程
空闲时,GUI线程
才会运行。
事件循环
- 同步任务
所有的同步任务
都在主线程
javascript引擎线程上执行,只有上一个任务执行完毕,下一个任务才会开始执行。
- 异步任务
异步任务
经对应的异步线程
处理后进入任务队列
。比如,setTimeout
经定时器线程
处理后,其回调函数将被放入任务队列
。
所有同步任务
都在主线程
上执行,因此形成一个执行栈
。
主线程
在运行的过程中,遇异步任务
会将其交给对应的异步线程
。经异步线程
处理后异步任务
会被放入任务队列
;主线程
执行完所有同步任务
,空闲时,会从任务队列
中读取异步任务
,此时,异步任务
将结束等待状态进入执行栈
,开始在主线程
上执行。
1、2重复,就是事件循环
。
举个简单的例子吧。
setTimeout(bless,0);
function sayHi(){
console.log("hello world");
}
function bless(){
console.log("have a nice day");
}
sayHi();
console.log("nice to meet you");
console.log("nice to meet you too");
console.log("how are you");
console.log("I'm fine");
console.log("thank you");
console.log("and you");
这里,bless
就是一个异步任务
,sayHi
和后面一群console.log
就是同步任务
,所以"have a nice day"
会在最后输出。