开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第8天,点击查看活动详情
一、进程和线程
进程
进程是
CPU资源分配的最小单位
对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程。就像一个工厂。
线程
线程(thread)是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。
有些进程还不止同时干一件事,比如Word,它可以同时进行打字、拼写检查、打印等事情。就像工厂里边的工人,
二、Js为什么事单线程
js诞生之初主要是运行在浏览器中的。主要是进行DOM的操作和页面交互。当两个 JavaScript脚本同时修改页面的同一个 DOM节点时,浏览器该执行哪个呢?所以设计支出就做成了单线程。
三、函数调用栈与任务队列
函数调用栈
执行上下文之后,根据规则储存起来的队列。栈底永远都是全局上下文,而栈顶就是当前正在执行的上下文。
栈:先进后出。把它想象成一个羽毛球盒子。
任务队列
可以理解成大家排队做核酸,先进先出。
四、宏任务和微任务
宏任务
script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering
微任务
process.nextTick, Promise, Object.observe(已废弃), MutationObserver(html5新特性)
在微任务中 process.nextTick 优先级高于Promise
在事件循环中,先进入全局环境。区分宏任务和微任务。第二次执行时,先执行所有的微任务,微任务执行完之后,在执行所有的宏任务。
五、案例解说
setTimeout(function() {
console.log('timeout1');
})
new Promise(function(resolve) {
console.log('promise1');
for(var i = 0; i < 1000; i++) {
i == 99 && resolve();
}
console.log('promise2');
}).then(function() {
console.log('then1');
})
console.log('global1');
- 从全局环境进入,发现setTimeout,是一个宏任务,把它放到宏任务的列表中:timeout1
- 继续往下走,发现了Promise.Promise构造函数会立即执行,所以在global中,输出promise1,promise2。
- 在执行中调用了resolve(),回调函数then1,进入微任务中:then1
- 发现console,执行输出global1。
- 这样第一次的循环完成,进入所有的微任务中,发现then1,然后输出then1
- 检查微任务是否执行完成。OK已完成,执行宏任务,发现timeout1,然后输出timeout1
- 检查所有的宏任务是否执行完成,OK已完成,调用栈空了,任务结束。
结果
promise1,promise2,global1,then1和timeout1
总结
- 从全局任务
script开始,任务依次进入栈中,被主线程执行,执行完后出栈。遇到不同的任务,我们进行区分宏任务和微任务 - 当调用栈为空,同步任务任务执行完毕时,会先执行微任务队列里的任务,微任务队列里的任务全部执行完毕后,会读取宏任务队列中拍最前的任务
- 执行宏任务的过程中,遇到微任务,依次加入微任务队列,当调用栈为空后,再次读取微任务队列里的任务,依次类推