1.引言
之前在这篇文章JS核心知识点梳理——异步,单线程,运行机制里面讲过一些eventloop,宏任务,微任务的概念,但是当时理解有限,有些东西讲的有点问题,有些东西讲的角度不同,也不够细,最近也是做了大量的异步题目,脑子里建立了一套新的,可靠的模型。今天借这个机会再次深入讲一下,并通过做大量面试题加深理解
2.全面了解浏览器
首先还是得介绍一下浏览器中的进程,线程概念。重点做到合理理解JS是单线程
这句话的意思
2.1浏览器有许多进程:
- Browser进程:浏览器的主进程(负责协调、主控),只有一个。
- 第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建
- GPU进程:最多一个,用于3D绘制等
- 浏览器渲染进程(浏览器内核)(Renderer进程,内部是多线程的)
2.2在浏览器渲染进程中有许多线程:
- 渲染引擎线程:顾名思义,该线程负责页面的渲染
- JS引擎线程:负责JS的解析和执行(
主线程
) - 定时触发器线程:处理定时事件,比如setTimeout, setInterval
- 事件触发线程:处理DOM事件
- 异步http请求线程:处理http请求
虽然JavaScript是单线程的(说的是JS引擎线程),可是浏览器内部不是单线程的。一些I/O操作、定时器的计时和事件监听(click, keydown...)等都是由浏览器提供的其他线程来完成的。
主线程和渲染引擎线程互斥,因为渲染的时候主线程可能通过dom操作渲染结果,所以主线程必须被阻塞
3.宏任务,微任务
直接记结论吧,没啥好说的
宏任务(macrotask):主代码块,setTimeout,setInterval等
微任务 (microtask):Promise,process.nextTick等
4.eventloop
js执行机制,也是Js是单线程但是不会阻塞的原因所在
我是这么理解的eventloop的(目前做的题都符合我这个模型)
1.主栈代码执行
2.遇到异步宏任务,给宏任务线程(比如定时器线程)处理,主栈继续往下执行同步代码
3.遇到异步微任务,给微任务线程处理,主栈代码继续往下执行同步代码
4.宏任务微任务线程一旦执行完,将相应的回调放到宏任务,微任务队列里面
5.主栈代码执行完
先看微任务队列有没有任务(也就是相应的回调),有就放到主栈里面执行执行完继续看微任务队列里面还有没有任务,直到微任务队列为空
6.查看宏任务队列里面有没有任务(回调),有的话取出来放到主栈执行
7.无线重复5-6 这个就是eventloop
5.面试题
5.1 求下面的打印结果
先来一道经典开胃菜,promise与setTimeout混合的题目
微任务里面套宏任务,宏任务里面套微任务
大家严格按照我章节4中提到的eventloop去分析,没什么难的
Promise.resolve().then((res) => {
setTimeout(() => {
console.log(1)
})
console.log(2)
})
setTimeout(() => {
Promise.resolve().then(() => {
console.log(3)
})
console.log(4)
}
)
答案:2 4 3 1
5.2 求下面的打印结果
再来个类似的,我自己编的
console.log('1');
setTimeout(function() {
new Promise(function(resolve) {
console.log('2');
resolve();
}).then(function() {
console.log('3')
})
console.log('4');
})
new Promise(function(resolve) {
console.log('5');
resolve();
}).then(function() {
console.log('6')
})
setTimeout(function() {
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
setTimeout(function(){
console.log('9')
})
})
答案:1 5 6 2 4 3 7 8 9
5.3 求下面的打印结果
头条公司的面试题,这回加上了async await
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('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
简单解析:
简单来说,await前面的是同步,await后面的是微任务,如果不懂为什么这样的请看我这篇文章async-await
都知道async和await的表现,结合eventloop,宏任务,微任务应该不难得出答案吧
答案:
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
5.4 求下面的打印结果
5.3的变种
无法也就是微任务里面套宏任务,宏任务里套微任务,类似5.1
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
//async2做出如下更改:
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise3');
resolve();
}).then(function() {
console.log('promise4');
});
console.log('script end');
答案:
script start
async1 start
promise1
promise3
script end
promise2
async1 end
promise4
setTimeout
5.5 求下面的打印结果
5.3的变种
比上一题还简单
async function async1() {
console.log('async1 start');
await async2();
//更改如下:
setTimeout(function() {
console.log('setTimeout1')
},0)
}
async function async2() {
//更改如下:
setTimeout(function() {
console.log('setTimeout2')
},0)
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout3');
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
答案
script start
async1 start
promise1
script end
promise2
setTimeout3
setTimeout2
setTimeout1
5.6求下面的打印结果
5.3变种,额外考察了点promise的原理 promise不懂的请移步到这promise
async function a1 () {
console.log('a1 start')
await a2()
console.log('a1 end')
}
async function a2 () {
console.log('a2')
}
console.log('script start')
setTimeout(() => {
console.log('setTimeout')
}, 0)
Promise.resolve().then(() => {
console.log('promise1')
})
a1()
let promise2 = new Promise((resolve) => {
resolve('promise2.then')
console.log('promise2')
})
promise2.then((res) => {
console.log(res)
Promise.resolve().then(() => {
console.log('promise3')
})
})
console.log('script end')
答案:
script start
a1 start
a2
promise2
script end
promise1
a1 end
promise2.then
promise3
setTimeout
6.结语
建议大家一定要把我收集的面试题做了,这样才能彻底检验你是否弄懂了异步。
如果面试题哪里有不太明白的地方,请在留言区指出,我会添加详细的分析步骤。
如果你觉得本文对你有很大的帮助,求点赞,求收藏,求打赏,你们的支持是作者写作的最大动力!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。