一.线程机制和事件机制
- 进程和线程的关系
1) 进程:程序的一次执行, 它占有一片独有的内存空间(比如打开王者荣耀,单进程只能执行一个,多进程能执行多个,王者荣耀是单进程,浏览器都是多进程)
2) 线程: CPU的基本调度单位, 是程序执行的一个完整流程
3.)进程与线程
一个进程中一般至少有一个运行的线程: 主线程
一个进程中也可以同时运行多个线程, 我们会说程序是多线程运行的
一个进程内的数据可以供其中的多个线程直接共享
多个进程之间的数据是不能直接共享的
个人理解:判断单进程和多进程的区别在于,能不能同时开启多个应用,能同时开启多个应用是多进程,否则为单进程;对于单线程和多线程,在一个时刻能干多件事,能则是多线程,否则为单线程(理解不对请指正)。
-
浏览器内核
支持浏览器运行的最核心的程序,也就是说浏览器也是一堆代码,有着其中最核心一部分代码,被称为浏览器内核。
内核由很多模块组成
1)html,css文档解析模块 : 负责页面文本的解析,
2)dom/css模块 : 负责dom/css在内存中的相关处理
3)布局和渲染模块 : 负责页面的布局和效果的绘制
4)定时器模块 : 负责定时器的管理
5)网络请求模块 : 负责服务器请求(常规/Ajax)
6)事件响应模块 : 负责事件的管理 -
定时器的思考
1.)定时器真是定时执行的吗?
定时器并不能保证真正定时执行
一般会延迟一丁点(可以接受), 也有可能延迟很长时间(不能接受)
2) 定时器回调函数是在分线程执行的吗?
在主线程执行的, js是单线程的
// 获取按钮
document.getElementById('btn').onclick = function () {
var start = Date.now()// 获取当前的时间(是从1970年1月1日开始计算)
console.log('启动定时器前...')
setTimeout(function () {
console.log('定时器执行了', Date.now()-start)
}, 200)
console.log('启动定时器后...')
// 做一个长时间的工作
for (var i = 0; i < 1000000000; i++) {
}
}
代码执行的顺序,先执行初始化代码,然后再执行定时器中的回调函数,在上面的代码中,如果初始化代码执行的时间远超于定时器定时的时间,这个时候定时器也不会执行,而是当初始化代码执行完之后再执行定时器的回调函数,你会发现至少2秒过去后,才执行定时器的回调函数,所以定时器不能保证定时执行。
- js是单线程
1)如何证明js执行是单线程的?(上面代码就可以证明)
setTimeout()的回调函数是在主线程执行的
定时器回调函数只有在运行栈中的代码全部执行完后才有可能执行
2) 为什么js要用单线程模式, 而不用多线程模式?
JavaScript的单线程,与它的用途有关。
作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。
这决定了它只能是单线程,否则会带来很复杂的同步问题;如果是多线程的话,两个线程同时进行操作一个dom元素,当第一个线程修改dom元素的一半的时候,第二个线程开始运行,对当前dom元素进行删除,完了之后第一个线程进行修改的时候,发现dom元素已经没了,就会出错,所以js采取了单线程,只有当修改完之后才能进行删除;当然多线程也有解决的办法,就是将线程锁住,当第一个线程执行完才能执行第二个线程。这样也可以解决问题,但是编写会更加复杂。(该解释是参考视频解析,对于作者去理解js为什么是单线程)
3) 代码的分类:
初始化代码
回调代码
4) js引擎执行代码的基本流程
先执行初始化代码: 包含一些特别的代码 回调函数(异步执行)
设置定时器
绑定事件监听
发送ajax请求
后面在某个时刻才会执行回调代码 - 事件循环模型
模型运转的流程:
1)执行初始化代码, 将事件回调函数交给对应模块管理
2)当事件发生时, 管理模块会将回调函数及其数据添加到回调列队中
3)只有当初始化代码执行完后(可能要一定时间), 才会遍历读取回调队列中的回调函数执行
- Web Workers
1)作用:分线程的实现,当需要进行大量的计算的时候,单线程页面会发生冻结,就是点击哪里都不会有反应,是因为只有一个线程,线程正在进行计算,没空鸟你;它的实现可以将大量的计算丢在分线程进行计算,然后将结果返回,不会影响主线程的执行,这样当进行大量计算的时候,也不会发生冻结。
2) 相关API
Worker: 构造函数, 加载分线程执行的js文件
Worker.prototype.onmessage: 用于接收另一个线程的回调函数
Worker.prototype.postMessage: 向另一个线程发送消息
3.)不足
worker内代码不能操作DOM(因为分线程的对象不是window,操作不了dom)
不能跨域加载JS(不了解,代码在vscode执行需要安装live server插件打开才不会出错)
不是每个浏览器都支持这个新特性
多给分线程传递数据和接受分线程的数据,速度要比原来的单线程慢。
主线程代码:
<input type="text" placeholder="数值" id="number">
<button id="btn">计算</button>
<script type="text/javascript">
var input = document.getElementById('number')
document.getElementById('btn').onclick = function () {
var number = input.value
//创建一个Worker对象
var worker = new Worker('worker.js')
// 绑定接收消息的监听
worker.onmessage = function (event) {
console.log('主线程接收分线程返回的数据: '+event.data)
alert(event.data)
}
// 向分线程发送消息
worker.postMessage(number)
console.log('主线程向分线程发送数据: '+number)
}
// console.log(this) // window
分线程代码(另外的js文件)
function fibonacci(n) {
//实现1,1,2,3,5,8..... 求斐波那契数列的第n个数
return n<=2 ? 1 : fibonacci(n-1) + fibonacci(n-2) //递归调用
}
console.log(this)
this.onmessage = function (event) {
var number = event.data// 为主线程发送过来的数据
console.log('分线程接收到主线程发送的数据: '+number)
//计算
var result = fibonacci(number)
postMessage(result)
console.log('分线程向主线程返回数据: '+result)
// alert(result) alert是window的方法, 在分线程不能调用
// 分线程中的全局对象不再是window, 所以在分线程中不可能更新界面
}
图解