JS 单线程
先聊聊JS的单线程工作,单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
那怎么样解决这种问题呢,在浏览器平台下,浏览器底层开启了多线程去执行了一些任务,形成了我们常说的异步任务,比如说定时器setTimeout
,异步任务可以很好的解决代码阻塞问题,但这也会带来代码的执行顺序没有像同步任务那样直观明了,这也是我们本文的重点。
JS 运行机制
一开始整个脚本作为一个宏任务执行。执行过程中同步代码直接执行,宏任务等待时间到达或者成功后,将方法的回调放入宏任务队列中,微任务进入微任务队列。
当前主线程的宏任务执行完出队,检查并清空微任务队列。接着执行浏览器 UI 线程的渲染工作,检查web worker 任务,有则执行。
然后再取出一个宏任务执行。以此循环...
EventLoop 是一种循环机制 ,不断去轮询一些队列 ,从中找到需要执行的任务并按顺序执行的一个执行模型。
宏任务与微任务
宏任务可以理解为每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)。
浏览器为了让 JS 内部宏任务 与 DOM 操作能够有序的执行,会在一个宏任务执行结束后,在下一个宏任务执行开始前,对页面进行重新渲染。
宏任务包含:script(整体代码)、setTimeout、setInterval、I/O、UI交互事件、MessageChannel 等。
微任务可以理解是在当前任务执行结束后需要立即执行的任务。也就是说,在当前任务后,在渲染之前,执行清空微任务。
所以它的响应速度相比宏任务会更快,因为无需等待 UI 渲染。
微任务包含:Promise.then、MutaionObserver、process.nextTick(Node.js 环境)等。
代码示例
console.log('start')
setTimeout(() => {
console.log('s1')
Promise.resolve().then(() => {
console.log('p2')
})
Promise.resolve().then(() => {
console.log('p3')
})
})
Promise.resolve().then(() => {
console.log('p1')
setTimeout(() => {
console.log('s2')
})
setTimeout(() => {
console.log('s3')
})
})
console.log('end')
代码分析EventLoop
- 首先遇到
console.log('start')
,输出start
,继续执行。 - 遇到
setTimeout
,由于setTimeout
是宏任务,时间到了将其放入宏任务队列中,继续执行。 - 遇到
Promise.then
,由于当前是微任务,将其放入微任务队列中,继续执行。 - 遇到
console.log('end')
,输出end
。 - 此时,当前主线程中的任务已全部执行完毕,
EventLoop
就开始工作,找需要执行的任务压入到执行栈中,微任务优先于宏任务,因此先把Promise.resolve().then
压入到执行栈开始继续执行。 - 遇到
console.log('p1')
,输出p1
,继续执行。 - 遇到两个
setTimeout
s2
,s3
, 放入到宏任务队列中等待执行。 - 此时,当前主线程中的任务又全部执行完毕,检查并清空微任务队列,由于此时没有微任务,然后就取出之前第2步骤的宏任务
setTimeout
开始执行。 - 输出
s1
,紧接着又遇到p2
,p3
两个微任务,将其放入微任务队列中。 - 此时,当前主线程中的任务又全部执行完毕,检查并清空微任务队列。所以相继输出
p2
,p3
。 - 此时,当前主线程中的任务又全部执行完毕,检查并清空微任务队列,由于此时没有微任务,然后就取出之前第7步骤的宏任务
setTimeout
开始执行。所以相继输出s2
,s3
。 - 整个js代码已全部完毕,最终输出顺序
start
,end
,p1
,s1
,p2
,p3
,s2
,s3
。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!