进程和线程
线程和进程是操作系统中的两个概念
- 进程(process):计算机已经运行的程序,是操作系统管理程序的一种方式
- 线程(thread):操作系统能够运行运算调度的最小单位,通常被包含在进程中
总结定义
- 进程:可以认为,启动一个应用程序,就会默认启动一个进程(也可能是多个进程)
- 线程:每一个进程中,都会启动至少一个线程用来执行程序中的代码,这个线程被称之为主进程
- 可以说进程是线程的容器
形象的例子
- 操作系统类似大工厂
- 工厂中有很多车间,每个车间是一个进程
- 每个车间都有一个以上的工人在工作,这个工人就是线程
操作系统的工作方式
- 操作系统如何让多个进程同时工作
- CPU的运算速度非常快,可以快速在多个进程之间切换
- 当进程中的线程获取到时间片时,就可以快速执行代码
- 速度很快,用户感受不到进程切换
浏览器中JS线程
- JS是单线程,JS的线程有自己的容器进程:浏览器/Node
- 多数浏览器是多进程的
- 每打开一个新的tab页面就会开启一个新进程,
- 防止一个页面卡死造成所有页面无法响应,需要强制退出浏览器
- 浏览器中每个进程中有很多线程,包括执行JS代码的线程
- JS的代码执行是在一个单独的线程中执行的,
- JS代码在同一个时刻只能做一件事,
- 如果这件事非常耗时,当前线程就会被阻塞
- 真正耗时的操作并不是由JS线程在执行的
- 由当前进程的其他线程完成这个耗时的操作
- 比如:网络请求,定时器...
- 我们只需在特定时间执行回调即可
- 由当前进程的其他线程完成这个耗时的操作
浏览器的事件循环
- 在执行JS代码的过程中,有异步函数调用,这个函数会被放入到调用栈中,执行会立即结束,并不会阻塞后续代码执行
浏览器的宏任务和微任务
- 事件循环中并非只维护着一个队列,事实上是有两个队列
- 宏任务队列(macrotask queue):ajax,setTimeout,setInterval,DOM监听,UI Rendering......
- 微任务队列(micritask queue):Promise的then回调,Mutation Observer API,queueMicrotask().......
- 事件循环对于两个队列的优先级
- main script中的代码优先执行(编写的是顶层script代码)
- 在执行任何一个宏任务之前(不是队列,是一个宏任务),都会先查看微任务队列中是否有任务需要执行
- 宏任务执行之前必须保证微任务队列是空的
- 如果不为空,那么就优先执行微任务队列中的任务(回调)
Node的事件循环
- 浏览器的EventLoop是根据HTML5定义的规范来实现的,不同的浏览器可能会有不同的实现
- Node中的EventLoop是由libuv实现的
- Node的架构图
- libuv中主要维护了一个EventLoop和worker threads(线程池)
- EventLoop负责调用系统的一些其他操作:文件的IO,Network,child-procecsses等
- libuv是一个多平台的专注于异步IO的库,它最初是为Node开发的,但是现在也被使用到Luvit,Julia,pyuv等其他地方
Node事件循环的阶段
- 事件循环像一个桥梁,是连接着应用程序的JS和系统调用之间的通道
- 无论是文件IO,数据库,网络IO,定时器,子进程,在完成对应的操作后,都会将对应的结果和回调函数放到事件循环(任务队列)中
- 事件循环会不断的从任务队列中取出对应的事件(回调函数)来执行
- 一次完整的事件循环Tick分成很多阶段
- 定时器(Timers):本阶段执行已经被setTImeout()和setInterval()的调度回调函数
- 待定回调(Pending Callback):对某些系统操作(如TCP错误类型)执行回调,比如TCP连接时接收到ECONNREFUSED
- idle,prepare:仅系统内部使用
- 轮询(Poll):检索新的I/O事件;执行与I/O相关的回调
- 检测(check):setImmediate()回调函数在这里执行
- 关闭的回调函数:一些关闭的回调函数,如socket.on('close', ...)
Node的宏任务和微任务
- 宏任务(macrotask):setTimeout,setInterval,IO事件,setImmediate,close事件
- 微任务(microtask):Promise的then调用,process.nextTick,queueMicrotask
执行顺序
- 微任务队列
- next tick queue:process.nextTick
- other queue:Promise的回调,queueMicrotask
- 宏任务队列:
- timer queue:setTimeout,setInterval
- poll queue:IO事件
- check queue:setImmediate
- close queue: close事件
Promise面试题
promise
setTimeout(function () {
console.log('set1');
new Promise(function (resolve) {
resolve()
}).then(function () {
new Promise(function(resolve) {
resolve()
}).then(function () {
console.log('then4');
})
console.log('then2');
})
});
new Promise(function(resolve) {
console.log('p1');
resolve()
}).then(function() {
console.log('then1');
})
setTimeout(function() {
console.log('set2');
});
console.log(2)
queueMicrotask(() => {
console.log('queueMicrotask');
})
// p1
// 2
// then1
// queueMicrotask
// set1
// then2
// then4
// set2
promise async await
// 字节面试题
async function async1() {
console.log('a1 start');
await async2()
console.log('a1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
async1()
new Promise (function(resolve) {
console.log('promise1');
resolve()
}).then(function () {
console.log('promise2');
})
// script start
// a1 start
// async2
// promise1
// a1 end
// promise2
// setTimeout
promise较难
- 原生promise在return thenable时会推迟一次微任务
- 原生promise在return Promise时会推迟一次微任务,如果Promise.resolve(4).then(),则会再推迟一次
Promise.resolve().then(() => {
console.log(0);
// 1.直接return一个值 相当于resolve(4)
// return 4 // 0 1 4 2 3 5 6
// 2.return thenbale的值 推到下一次的微任务中
// return {
// then(resolve) {
// resolve(4) // 0 1 2 4 3 5 6
// }
// }
// 3.return Promise 不是普通的值,会多加一次微任务
// Promise.resolve(4),再多加一次微任务
return Promise.resolve(4) // 0 1 2 3 4 5 6
}).then((res) => {
console.log(res);
})
Promise.resolve().then(() => {
console.log(1);
}).then(() => {
console.log(2);
}).then(() => {
console.log(3);
}).then(() => {
console.log(5);
}).then(() => {
console.log(6 );
})
Node事件循环面试题
async function async1() {
console.log('async1 start');
await async2()
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout0');
}, 0);
setTimeout(function() {
console.log('setTimeout2');
}, 300);
setImmediate(() => console.log(setImmediate))
process.nextTick(() => {console.log('nextTick1')})
async1()
process.nextTick(() => {console.log('nextTick2');})
new Promise(function(resolve) {
console.log('promise1');
resolve()
console.log('promise2');
}).then(function() {
console.log('promise3');
})
console.log('script end');
// script start
// async1 start
// async2
// promise1
// promise2
// script end
// nextTick1
// nextTick2
// async1 end
// promise3
// setTimeout0
// [Function: setImmediate] {
// [Symbol(nodejs.util.promisify.custom)]: [Getter]
// }
// setTimeout2