浏览器中的JavaScript线程
我们经常会说JavaScript是单线程(可以开启workers)的,但是JavaScript的线程应该有自己的容器进程:浏览器或者Node。
Js是单线程
- js是单线程的。也就是说,同一时间只能做一件事。(有些任务是耗时的,会阻塞代码的执行)
- 作为浏览器脚本语言,与它的用途有关。
- js的主要用途是和用户互动,以及操作DOM。这决定了它只能是单线程。
同步、异步
可以把代码分为同步代码(同步任务)和异步代码(异步任务)
-
同步代码:立即放入js引擎(js主线程)执行,并原地等待结果。
-
异步代码:先放入宿主环境(浏览器/Node),不必原地等待结果,并不阻塞主线程继续往下执行,异步结果在将来执行。比如setTimeout、setInterval、Ajax/Fetch、事件绑定等。
浏览器的线程
浏览器是一个进程吗,它里面只有一个线程吗?
-
目前多数的浏览器其实都是多进程的,当我们打开一个tab页面时就会开启一个新的进程,这是为了防止一个页面卡死而造成所有页面无法响应,整个浏览器需要强制退出;
-
每个进程中又有很多的线程,其中包括执行JavaScript代码的线程;
由于JavaScript的代码执行是在一个单独的线程中执行的,这意味着JavaScript的代码,在同一个时刻只能做一件事; 如果这件事是非常耗时的,就意味着当前的线程就会被阻塞;所以真正耗时的操作,实际上并不是由JavaScript线程在执行的,因为我们的浏览器每个进程是多线程的,那么其他线程可以完成这个耗时的操作。
比如网络请求、定时器,我们只需要在特性的时候执行应该有的回调即可;如果在执行JavaScript代码的过程中有异步操作,比如中间我们插入了一个setTimeout函数调用。这个函数被放到
调用栈中,执行会立即结束,并不会阻塞后续代码的执行。
function sum(){
.....
}
setTimeout(()=>{
console.log('xxx')
},1000)
other code....
-
Js是单线程,防止代码阻塞,把代码(任务)分为:同步和异步
-
同步代码给js引擎执行,异步代码交给宿主环境
-
同步代码放入执行栈中,异步代码等待时机成熟送入任务队列排队
-
执行栈执行完毕,会去任务队列看是否有异步任务,有就送到执行栈执行,反复循环查看执行,这个过程就是事件循环(eventloop)
宏任务与微任务
但是事件循环中并非只维护着一个队列,事实上是有两个队列, 异步的代码才有宏任务与微任务。
-
宏任务队列(macrotask queue):ajax、setTimeout、setInterval、DOM监听、UI Rendering等
-
微任务队列(microtask queue):Promise的then回调、** Mutation Observer API**、**queueMicrotask()**等
那么事件循环对于两个队列的优先级是怎么样的呢?
1.main script中的代码优先执行(编写的顶层script代码);
2.在执行任何一个宏任务之前(不是队列,是一个宏任务),都会先查看微任务队列中是否有任务需要执行
也就是宏任务执行之前,必须保证微任务队列是空的;
如果不为空,那么就优先执行微任务队列中的任务(回调)