进程与线程
进程(process):程序的一次执行,占有独有的一片内存空间;可以通过windows任务管理器查看进程。
线程(thread):是进程内的一个独立执行单元,是程序执行的一个完整流程,是CPU最小的调度单元。
- 一个进程内只有一个线程的程序,就是单线程程序;
- 一个进程中可以同时运行多个线程,称为多线程程序;
- 应用程序必须运行在某个进程的某个线程上;
- 一个进程中至少有一个运行的线程:主线程,是进程启动后自动创建的
- 一个进程内的数据可供其中的多个线程直接共享
- 多个进程之间的数据是不能直接共享的(互相独立)
多线程的优点:能有效提高cpu的利用率;缺点:创建多线程开销;线程间切换开销;状态同步问题。
单线程优点:顺序编程简单;缺点:效率低。
- Chrome,Safari:webkit
- Firefox:Gecko
- IE:Trident
JavaScript事件循环机制
JavaScript的一大特点是单线程,这个线程中有唯一的一个事件循环。
这个事件循环中会有一个正在执行的任务(Task),而这个任务就从macrotask队列中来的。当这个macrotask队列中的任务执行结束后会取出microtask中的任务,然后在同一个事件循环中执行。
- macrotasks:
setTimeout
、setInterval
、I/O等 - microtasks: Promise
一些注意点:
- 一个事件循环(event loop)会有一个或多个任务队列(task queue),任务队列就是macrotask queue
- 每一个event loop都有一个microtask queue
- task queue == macrotask queue != microtask queue
- 事件循环的顺序,决定了JavaScript代码的执行顺序。从整体代码开始第一次循环。从macrotask开始,执行完macrotask再执行microtask,当所有的microtask执行完毕之后再次从macrotask开始,这样一直循环下去。
分析一段代码:
console.log('start')
setTimeout(() => {
console.log('setTimeout 1')
Promise.resolve()
.then(() => {
console.log('promise 3')
})
.then(() => {
console.log('promise 4')
})
.then(() => {
setTimeout(() => {
console.log('setTimeout 2')
Promise.resolve()
.then(() => {
console.log('promise 5')
})
.then(() => {
console.log('promise 6')
})
}, 0)
})
}, 0)
Promise.resolve()
.then(() => {
console.log('promise 1')
})
.then(() => {
console.log('promise 2')
})
// 运行结果
// start
// promise 1
// promise 2
// setTimeout 1
// promise 3
// promise 4
// setTimeout 2
// promise 5
// promise 6
对这段代码进行一下分析:
loop1
- setTimeout的回调(setTimeout 1)进入macrotask
- promise的两个then进入microtask(promise 1 和 promise 2)
- 当前执行环境执行结束,开始执行microtask queue,输出promise 1 和 promise 2
loop2
- 先执行macrotask,先输出setTimeout 1
- 接下来的三个promise进入microtask,当前执行环境已空,则执行microtask,输出promise 3 和 promise 4,第三个promise(setTimeout 2)又一次进入了macrotask queue
loop3
- 同样,macrotask,输出setTimeout 2,两个promise的then进入microtask
- 执行microtask,输出promise 5 和 promise 6