1.什么是线程和进程,浏览器是多进程的,为什么设计成多线程
2.什么是异步,有哪些异步任务,js遇到异步如何执行
3.浏览器的事件循环及面试题
4.node的事件循环和面试题
1.什么是线程和进程,浏览器是多进程的,为什么设计成多线程
进程就是一个应用程序,线程就是操作系统最小调度单位,一个进程中可以有多个线程,一个应用也可以有多个线程,比如浏览器就是多进程的,为了防止一个tab页卡死导致其他tab页卡死,简单理解就是一个电脑相当于工厂,一个应用程序相当于一个车间,一个进程相当于车间的一个流水线,一个线程相当于流水线的工人
补充:操作系统如果是单核得,操作系统得工作方式就是单个核在快速得在不同得进程中切换执行代码
2.什么是异步,有哪些异步任务,js遇到异步如何执行
异步就是执行一段代码下面得代码不需要等待上面得代码执行完,就是可以同时处理多个代码
异步任务有dom操作 定时器 网络请求 promise的then
//从执行来看回调函数和同步异步没关系 行为来看是异步 因为他在另外一个函数里面其实是不知道什么时候会被调用的
function Namee(params) {
params()
console.log(12);
}
Namee(()=>{console.log(23);})
// 23
// 12
//不觉得yield和异步有关系
function* foo() {
console.log(23)
yield 2
}
let bar=foo()
function Namee(params) {
bar.next()
console.log(12);
}
Namee()
// 23
// 12
//可知promise得then是异步
function Namee(params) {
let bar=new Promise((r,j)=>{
console.log(23);
r(1)
}).then(()=>{
console.log(4);
})
console.log(12);
}
Namee()
// 23
// 12
//4
对于点击事件的监听代码执行是同步,行为异步 不过dom监听里面的函数比直接绑定的要后执行
对其理解比较赞同的是https://www.cnblogs.com/AFu-1993/p/12319673.html这个文章
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button onclick="console.log(99999)">按钮1</button>
<script>
let btn=document.getElementsByTagName('button')
btn[0].addEventListener('click',()=>{
console.log(888);
})
btn[0].click()
console.log(76666);
//99999
//888
//76666
//点击事件的监听是属于同步的
</script>
</body>
</html>
js是单线程得,当他遇到异步得时候,会把异步代码放入到其他线程进行执行,直到执行完,再对执行回调
3.浏览器的事件循环及面试题
1)浏览器的事件循环就是 当浏览器碰到异步任务的时候,会把异步代码放入其他线程进行执行,当执行完后放入队列里,坚持先进先出的原则,把回调放入js线程执行得到结果 队列又分为两种,分别是微任务队列和宏任务队列,每次进行宏任务队列前会先去检查微任务队列是否全部执行完成,执行完成后才会进行宏任务队列的执行
2)宏任务包括:ajax、setTimeout、setInterval、UI Rendering等
微任务队列包括:Promise的then回调、 Mutation Observer API、queueMicrotask()等
什么是Mutation Observer API:用来监视DOM变动,DOM的任何变动,如节点的增添、属性的变动、文本内容的变动,都可以得到通知,在概念上,通事件相近,但是本质不同是:事件时是同时触发,而Mutation Observer是异步触发,也就是DOM所有操作结束后,才触发。这样设计是为了解决DOM频繁修改的情况。
queueMicrotask()
:将微任务加入队列以在控制返回浏览器的事件循环之前的安全时间执行。
3)面试题:
setTimeout(function () {
console.log("setTimeout1");
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("promise1");
resolve();
}).then(function () {
console.log("then1");
});
setTimeout(function () {
console.log("setTimeout2");
});
console.log(2);
queueMicrotask(() => {
console.log("queueMicrotask1")
});
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log("then3");
});
//promise1 new Promise会先执行一次 同步直接打印
// 2 同步直接打印
// then1 执行宏任务前先看有没有微任务 promise的then是微任务
// queueMicrotask1 queueMicrotask是微任务
// then3 是微任务
// setTimeout1 微任务执行完了 所以执行宏任务setTimeout
// then2 宏任务里面碰到了微任务 没有其他同步 有微任务 所以setTimeout2不打印
// then4 微任务执行完又加入了微任务 所以setTimeout2不打印
// setTimeout2 宏任务
async function async1 () {
console.log('async1 start')
await async2();//加了await后相当于后面的默认放入了promise.then
console.log('async1 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')
})
console.log('script end')
// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// setTimeout
Promise.resolve().then(() => {
console.log(0);
// 1.直接return一个值 相当于resolve(4)
// return 4
// 2.return thenable的值
return {
then: function(resolve) {
// 大量的计算
resolve(4)
}
}
// 3.return Promise
// 不是普通的值, 多加一次微任务
// Promise.resolve(4), 多加一次微任务
// 一共多加两次微任务
return Promise.resolve(4)
}).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);
})
// 1.return 4
// 0
// 1
// 4
// 2
// 3
// 5
// 6
// 2.return thenable
// 0
// 1
// 2
// 4
// 3
// 5
// 6
// 3.return promise
// 0
// 1
// 2
// 3
// 4
// 5
// 6
4.node的事件循环和面试题
1)node事件循环:先从服务器上获取代码,v8进行执行,如果遇到了异步,就放入node的libuv里面的队列中,事件循环包括很多部分,一个部分一个部分的执行,每个部分执行的时候,会执行队列中需要执行的,在线程池中处理,执行完后出队列
2)事件循环包括五部分:
(1)定时器(Timers):本阶段执行已经被 setTimeout() 和 setInterval() 的调度回调函数。
(2)待定回调(Pending Callback):对某些系统操作(如TCP错误类型)执行回调,比如TCP连接时接收到
(3)idle, prepare:仅系统内部使用。
(4)轮询(Poll):检索新的 I/O 事件;执行与 I/O 相关的回调;是停留时间最多的一个阶段;
(5)检测(check):setImmediate() 回调函数在这里执行。
3)事件循环的顺序
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
// settimetout0
// setImmediate
// setTimeout2
5)setTimeout(回调函数, 0)、setImmediate(回调函数)执行顺序分析:
把setTimeout放入事件队列也是需要时间的,事件循环开启也需要时间,如果先开启了事件循环,再把setTimeout放入队列,就不会执行setTimeout,而是等到下一次tick再执行setTimeout
还需探究:dom监听怎么就是宏任务了 在上面的代码里面如果dom操作是宏任务 那么应该在打印76666前是无法打印出888,因为那时候应该还没有监听到dom的
订阅和发布也说是异步的,怎么样证明
对于以上:在自己测试的过程中,我觉得回调函数和事件监听属于事件同步,行为异步,不认为其算是异步,dom监听比如click是不属于宏任务