一、进程与线程
计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行。
假定工厂的电力有限,一次只能供给一个车间使用。也就是说,一个车间开工的时候,其他车间都必须停工。背后的含义就是,单个CPU一次只能运行一个任务。
进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。
一个车间里,可以有很多工人。他们协同完成一个任务。线程就好比车间里的工人。一个进程可以包括多个线程。
二、任务队列
Javascript从诞生就是单线程的,这意味着所有任务需要排队执行,前一个任务执行完了才会执行后面一个任务。如果前面任务执行时间过长,后面一直等着,这样太费时间了。
于是将任务分为两种,同步任务和异步任务,异步任务就是执行时间较长的任务。
- 将所有同步任务放在主线程上执行,形成一个执行栈,栈和队列不同,栈是先进后出,就像子弹夹
- 主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就把他放入任务队列中
- 一旦"执行栈"中的所有同步任务执行完毕,主线程空了,就去任务队列中读取任务,按照顺序将任务放入执行栈中执行
三、宏任务和微任务
异步任务又分为宏任务和微任务
- 宏任务:macro-task包括:script setTimeout, setInterval, setImmediate, I/O, UI rendering。
- 微任务:micro-task包括:process.nextTick, Promise, Object.observe, MutationObserver。
当执行栈中的任务都在执行完了,主线程空出来,他就去任务队列中读取异步任务。任务队列又分为宏任务队列和微任务队列。 在读取异步任务时,先读取微任务队列,如果不为空,就一次先执行完所有的微任务,完成之后再去读取宏任务队列,每执行完一个宏任务就去查看微任务队列是不是为空,不是的话执行完当前宏任务后再去执行微任务,如此循环。。。
四、举个栗子
1.先来个简单的栗子:
setTimeout(()=>{
console.log("setTimeout1");
Promise.resolve().then(data => {
console.log(222);
});
});
setTimeout(()=>{
console.log("setTimeout2");
});
Promise.resolve().then(data=>{
console.log(111);
});
结果:
111
setTimeout1
222
setTimeout2
2.复杂点的栗子:
console.log('script start');
setTimeout(function () {
console.log('setTimeout---0');
}, 0);
setTimeout(function () {
console.log('setTimeout---200');
setTimeout(function () {
console.log('inner-setTimeout---0');
});
Promise.resolve().then(function () {
console.log('promise5');
});
}, 200);
Promise.resolve().then(function () {
console.log('promise1');
}).then(function () {
console.log('promise2');
});
Promise.resolve().then(function () {
console.log('promise3');
});
console.log('script end');
结果:
script start
script end
promise1
promise3
promise2
setTimeout---0
setTimeout---200
promise5
inner-setTimeout---0