概述
js执行是单线程:(发送请求,接受请求,渲染页面,执行js等等这些就是一个个线程。)
JS引擎
通常讲到浏览器的时候,我们会说到两个引擎:渲染引擎和JS引擎。渲染引擎就是如何渲染页面,Chrome/Safari/Opera用的是Webkit引擎,IE用的是Trident引擎,FireFox用的是Gecko引擎。不同的引擎对同一个样式的实现不一致,就导致了经常被人诟病的浏览器样式兼容性问题。这里我们不做具体讨论。
JS引擎可以说是JS虚拟机,负责JS代码的解析和执行。通常包括以下几个步骤:
- 词法分析:将源代码分解为有意义的分词
- 语法分析:用语法分析器将分词解析成语法树
- 代码生成:生成机器能运行的代码
- 代码执行
不同浏览器的JS引擎也各不相同,Chrome用的是V8,FireFox用的是SpiderMonkey,Safari用的是JavaScriptCore,IE用的是Chakra。
之所以说JavaScript是单线程,就是因为浏览器在运行时只开启了一个JS引擎线程来解析和执行JS。那为什么只有一个引擎呢?如果同时有两个线程去操作DOM,浏览器是不是又要不知所措了。
所以,虽然JavaScript是单线程的,可是浏览器内部不是单线程的。一些I/O操作、定时器的计时和事件监听(click, keydown...)等都是由浏览器提供的其他线程来完成的。
一个浏览器通常由以下几个常驻的线程:
- 渲染引擎线程:顾名思义,该线程负责页面的渲染
- JS引擎线程:负责JS的解析和执行
- 定时触发器线程:处理定时事件,比如setTimeout, setInterval
- 事件触发线程:处理DOM事件
- 异步http请求线程:处理http请求
需要注意的是,渲染线程和JS引擎线程是不能同时进行的。渲染线程在执行任务的时候,JS引擎线程会被挂起。因为JS可以操作DOM,若在渲染中JS处理了DOM,浏览器可能就不知所措了。
同步执行和异步执行
- 同步:只有前一个任务执行完毕,才能执行后一个任务
- 异步:当同步任务执行到某个
WebAPI
时,就会触发异步操作,此时浏览器会单独开线程去处理这些异步任务。
任务队列、回调队列、事件循环
WebAPI
是啥?浏览器事件、定时器、ajax
,这些操作不会阻塞 JS 的执行,JS 会跳过当前代码,执行后续代码
- 任务队列( Task Queue ):主线程执行完毕后所触发的异步任务( WebAPIs ),叫任务队列
- 回调队列( Callback Queue ):这些异步 WebAPI 执行完成后得到的结果,会添加到 callback queue 中
- 事件循环( Event Loop ):只要主线程的同步任务执行完毕,就会不断的读取 "回调队列" 中的回调函数,到主线程中执行,这个过程不断循环往复
如何知道主线程执行执行完毕?JS引擎存在 monitoring process 进程,会持续不断的检查主线程执行为空,一旦为空,就会去 callback queue 中检查是否有等待被调用的函数。
console.log('1');
setTimeout(function() {
console.log('2');
}, 0);
console.log('3');
- 打印1
- 遇到 WebAPI(
setTimeout
) ,浏览器新开定时器线程处理,执行完成后把回调函数存放到回调队列中。专业一点的说发:JS
引擎遇到异步任务后不会一直等待其返回结果,而是将这个任务挂起交給其他浏览器线程处理,自己继续执行主线程中的其他任务。这个异步任务执行完毕后,把结果返回给回调队列。被放入的代码不会被立即执行。而是当主线程所有同步任务执行完毕, monitoring process 进程就会把 "回调队列" 中的第一个回调代码放入主线程。然后主线程执行代码。如此反复 - 打印3 异步
setTimeout
不会阻塞同步代码,因此会首先打印3 - 主线程执行完毕后,执行 Callback Queue 打印2
异步任务的执行优先级并不相同,它们被分为两类:微任务( micro task ) 和 宏任务( macro task ) 根据异步事件的类型,这些事件实际上会被派发对应的宏任务和微任务中,在当前主线程执行完毕后,
- 会先查看微任务中是否有事件存在,如果不存在,则再去找宏任务
- 如果存在,则会依次执行队列中的参数,直到微任务列表为空,让后去宏任务中一次读取事件到主线程中执行,如此反复 当前主线程执行完毕后,会首先处理微任务队列中的事件,让后再去读取宏任务队列的事件。在同一次事件循环中,微任务永远在宏任务之前执行。
- 宏任务( macro-task ):整体
script
、setTimeout
、setInterval
、UI交互事件
、I/O
- 微任务( micro-task ):
process.nextTick
、Promise
、MutaionObserver
(function test() {
setTimeout(function() {console.log(4)}, 0);
new Promise(function (resolve, reject) {
console.log(1);
for( var i=0 ; i<10000 ; i++ ) {
i == 9999 && resolve();
}
console.log(2);
}).then(function() {
console.log(5);
});
console.log(3);
})()
1. setTimeout:宏任务:存入宏任务队列
2. Promise:函数本身是同步执行的( **Promise** 只有一个参数,默认new的时候就会同步执行), `.then` 是异步,因此依次打印1、2
`.then` 存入微任务中
3. 打印3( 第一次主线程执行完毕 )
4. 执行微任务中的回调函数:5, 让后执行宏任务中的 `setTimeout` 4
// 最终结果1,2,3,5,4
最后
以上就是受伤学姐为你收集整理的js的单线程和浏览器的多线程的全部内容,希望文章能够帮你解决js的单线程和浏览器的多线程所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复