ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

13、JavaScript事件循环机制 - JavaScript学习系列文章

13、JavaScript事件循环机制 - JavaScript学习系列文章 一、为什么需要事件循环首先, 咱们得明白JavaScript是单线程的. 啥意思呢? 就是说JS在同一时间只能做一件事, 不能像Java那样开多个线程同时干活. 这听起来是不是很弱鸡? 别急, 设计成这样是有原因的.想象一下, 如果JS是多线程的, 你在一个线程里修改DOM, 另一个线程里删除同一个DOM, 那浏览器该听谁的? 这不就乱套了吗? 为了避免这种混乱, JS选择了单线程.但单线程也有个问题--如果遇到耗时操作(比如网络请求), 整个页面不就卡死了吗? 这时候事件循环机制就派上用场了! 它让JS虽然只有一个主线程, 但能够看起来同时处理多个任务.二、事件循环机制事件循环机制主要由三部分组成:1. 调用栈(Call Stack):这是JS执行代码的地方, 你可以把它想象成一个盘子, 后放进去的先拿出来(后进先出)2. 任务队列(Task Queue):等待执行的任务排成的队伍3. 事件循环(Event Loop):负责盯着调用栈和任务队列的监工咱们来看个简单的例子:functionmultiply(a, b) {return a * b;}functionsquare(n) {returnmultiply(n, n);}functionprintSquare(n) {const squared square(n);console.log(squared);}printSquare(4);执行过程是这样的:1. printSquare(4)入栈2. printSquare内部调用square(4)square(4)入栈3. square内部调用multiply(4,4)multiply(4,4)入栈4. multiply执行完返回16出栈5. square返回16出栈6. console.log(16)入栈执行完出栈7. printSquare执行完出栈整个过程就像叠盘子一样, 后进的先出.那异步代码怎么执行呢? 看这个例子:console.log(Hi);setTimeout(functioncb() {console.log(There);},5000);console.log(JS);输出顺序是: Hi - JS - There执行过程:1. console.log(Hi)入栈, 执行完出栈2. 遇到setTimeout,这是Web API提供的功能, JS把它交给浏览器处理, 同时注册回调函数cb3. console.log(JS)入栈, 执行完出栈4. 5秒后, 浏览器把cb放入任务队列5. 事件循环发现调用栈空了, 就把cb从任务队列拿到调用栈执行三、微任务与宏任务这才是重点! 面试常考! JS的任务其实分为两种:宏任务(Macrotask):包括script整体代码、setTimeout、setInterval、I/O、UI渲染等.微任务(Microtask): 包括Promise.then、MutationObserver、process.nextTick(Node环境).它们执行顺序的区别很重要:1. 执行一个宏任务(初始就是整个script)2. 执行过程中遇到微任务就加入微任务队列; 遇到宏任务就加入宏任务队列3. 当前宏任务执行完, 立即执行所有微任务(直到微任务队列清空)4. 进行浏览器渲染(如果需要)5. 开始下一个宏任务来看个经典例子:console.log(script start);setTimeout(function() {console.log(setTimeout);},0);Promise.resolve().then(function() {console.log(promise1);}).then(function() {console.log(promise2);});console.log(script end);输出顺序是:script start - script end - promise1 - promise2 - setTimeout解释:1. 首先执行整个script(这是一个宏任务)2. 遇到console.log(script start)直接执行3. 遇到setTimeout,回调加入宏任务队列4. 遇到Promise.then,第一个then回调加入微任务队列5. 执行console.log(script end)6. 当前宏任务执行完毕, 开始执行微任务队列7. 执行第一个then回调, 输出promise1, 然后又遇到第二个then, 其回调加入微任务队列8. 继续执行微任务队列输出promise29. 微任务队列清空开始下一个宏任务(setTimeout回调)输出setTimeout四、浏览器与Node环境差异1) 浏览器中的事件循环浏览器的事件循环相对简单:执行宏任务执行所有微任务可能需要渲染重复2) Node.js中的事件循环Node的事件循环分为多个阶段:timers阶段:执行setTimeout和setInterval回调I/O callbacks阶段:执行几乎所有的回调(除了close事件、timers和setImmediate)idle, prepare阶段:内部使用poll阶段:检索新的I/O事件执行相关回调check阶段:执行setImmediate回调close callbacks阶段:执行关闭事件的回调(如socket.on(close))在Node中,process.nextTick的优先级高于微任务Promise.then.来看个Node的例子:setTimeout(() console.log(timeout),0);setImmediate(() console.log(immediate));Promise.resolve().then(() console.log(promise));process.nextTick(() console.log(nextTick));输出顺序可能是:nextTick - promise - timeout - immediate解释:process.nextTick优先级最高然后是微任务Promise.then然后是timers阶段的setTimeout最后是check阶段的setImmediate
返回列表