1. 进程和线程
进程是表示资源分配的基本单位, 线程是进程中执行运算的最小单位, 亦即执行处理机调度的基本单位, 线程开销少, 易于调度, 可以提高并发性.
进程和线程的关系
进程和线程的联系
(1) 一个线程只能属于一个进程, 而一个进程可以有多个线程, 但至少有一个线程. 线程是操作系统可识别的最小执行和调度单位.
(2) 资源分配给进程, 同一进程的所有线程共享该进程的所有资源. 同一进程中的多个线程共享代码段 (代码和常量), 数据段 (全局变量和静态变量), 扩展段 (堆存储). 但是每个线程拥有自己的栈段, 栈段又叫运行时段, 用来存放所有局部变量和临时变量.
(3) 处理机分给线程, 即真正在处理机上运行的是线程.
(4) 线程在执行过程中, 需要协作同步. 不同进程的线程间要利用消息通信的办法实现同步.
举例: 浏览器中, 主要包括四个进程: Browser 主进程, 第三方插件进程, GPU 进程, 浏览器的渲染进程. 每打开一个 tab 页, 就会创建一个进程. 而在 tab 中进行的操作, 如 js 解析, 如向服务器发送请求, 则是属于这个进程中的一个线程中的操作.
浏览器渲染进程是多线程的, 主要包括 GUI 渲染线程 (包括解析 html,CSS 等),js 引擎线程, 事件触发线程, 定时触发器线程, 异步 http 请求线程. GUI 线程和 js 线程是互斥的, 当 js 引擎执行时 GUI 线程会被阻塞. 事件触发线程会把任务存储在队列中, 排队等 js 线程空闲时处理. 定时触发器线程会进行计时并触发 setIntervel 和 setTimeOut 定时, 触发后放在队列中等待 js 引擎处理. 异步 http 请求线程, 在 XMLHttpRequest 在连接后是通过浏览器新开一个线程请求, 将检测到状态变更时, 如果设置有回调函数, 异步线程就产生状态变更事件, 将这个回调再放入事件队列中. 再由 JavaScript 引擎执行.
事件循环
- console.log('script start');
- setTimeout(function() {
- console.log('setTimeout');
- }, 0);
- Promise.resolve().then(function() {
- console.log('promise1');
- }).then(function() {
- console.log('promise2');
- });
JS 中分为两种任务类型: macrotask 和 microtask, 在 ECMAScript 中, microtask 称为 jobs,macrotask 可称为 task
macrotask(又称之为宏任务), 可以理解是每次执行栈执行的代码就是一个宏任务 (包括每次从事件队列中获取一个事件回调并放到执行栈中执行)
每一个 task 会从头到尾将这个任务执行完毕, 不会执行其它
浏览器为了能够使得 JS 内部 task 与 DOM 任务能够有序的执行, 会在一个 task 执行结束后, 在下一个 task 执行开始前, 对页面进行重新渲染
microtask(又称为微任务), 可以理解是在当前 task 执行结束后立即执行的任务
也就是说, 在当前 task 任务后, 下一个 task 之前, 在渲染之前
所以它的响应速度相比 setTimeout(setTimeout 是 task) 会更快, 因为无需等渲染
也就是说, 在某一个 macrotask 执行完后, 就会将在它执行期间产生的所有 microtask 都执行完毕 (在渲染前)
macrotask: 主代码块, setTimeout,setInterval 等 (可以看到, 事件队列中的每一个事件都是一个 macrotask)
microtask:Promise,process.nextTick 等
- console.log('script end');
来源: http://www.qdfuns.com/article/42837/d9b73a4817a3f248d3cbe331b693921c.html