摘要:浏览器和中并不一样,浏览器的是在中定义的规范,而中则由库实现。整个的这种运行机制又称为事件循环例子了解浏览器的后,查看下面例子,猜测浏览器是怎么输出的浏览器输出中的在内部有这样一个事件环机制。在启动时会初始化事件环。执行和中到期的。
大家都知道,javascript是一门单线程语言,因此为了实现主线程的不阻塞,Event Loop这样的方案应运而生。
浏览器和node中Event loop并不一样,浏览器的Event loop是在HTML5中定义的规范,而node中则由libuv库实现。
浏览器中的Event loop
所有同步任务都在主线程上执行,形成一个执行栈
主线程之外,还存在一个任务队列。
任务队列分为macro-task(宏任务)和micro-task(微任务)。
macro-task(宏任务): setTimeout, setInterval, setImmediate, I/O等
micro-task(微任务): process.nextTick, 原生Promise(有些实现的promise将then方法放到了宏任务中),Object.observe(已废弃), MutationObserver等
整个最基本的Event Loop如图所示:
具体过程:
浏览器中,先执行当前栈,执行完主执行线程中的任务。
取出Microtask微任务队列中任务执行直到清空。
取出Macrotask宏任务中 一个 任务执行。
检查Microtask微任务中有没有任务,如果有任务执行直到清空。
重复3和4。
整个的这种运行机制又称为Event Loop(事件循环)
例子
了解浏览器的Event loop后,查看下面例子,猜测浏览器是怎么输出的
console.log(1); console.log(2); setTimeout(function(){ console.log("setTimeout1"); Promise.resolve().then(function(){ console.log("Promise") }) }) setTimeout(function(){ console.log("setTimeout2"); }) //浏览器输出:1 2 setTimeout1 Promise setTimeout2
node中的Event loop
在libuv内部有这样一个事件环机制。在node启动时会初始化事件环。
node中的event loop分为6个阶段,不同于浏览器的是,这里每一个阶段都对应一个事件队列,node会在当前阶段中的全部任务执行完,清空NextTick Queue,清空Microtask Queue,再执行下一阶段。
在node.js里,process 对象代表node.js应用程序,可以获取应用程序的用户,运行环境等各种信息。process.nextTick()方法将 callback 添加到next tick 队列,并且nextTick优先级比promise等microtask高。
timers:执行setTimeout() 和 setInterval()中到期的callback。
I/O callbacks:上一轮循环中有少数的I/Ocallback会被延迟到这一轮的这一阶段执行
idle, prepare:队列的移动,仅内部使用
poll:最为重要的阶段,执行I/O callback,在适当的条件下会阻塞在这个阶段
check:执行setImmediate的callback
close callbacks:执行close事件的callback,例如socket.on("close",func)
例子
查看下面例子加深对event loop的理解
在node执行下面代码,发现每次执行先后顺序不一样,因为node需要启动时间,执行过程中setTimeout可能到时间了也可能没到时间,所以这个先后顺序取决于node的执行时间。
setTimeout(function(){ console.log("timeout") }) setImmediate(function(){ console.log("immediate") })
i/o操作阶段完成后,会走check阶段,所以setImmediate会优先走
let fs=require("fs"); fs.readFile("./1.log",function(){ console.log("fs"); setTimeout(function(){ console.log("timeout") }) setImmediate(funciton(){ console.log("setTimmediate") }) })
nextTick应用场景
function Fn(){ this.arrs; process.nextTick(()=>{ //根据nextTick的特性,可以先赋值,再在下一个队列中使用 this.arrs(); }) } Fn.prototype.then=function(){ this.arrs=function(){console.log(1)} } let fn=new Fn(); fn.then(); //注意:nextTick千万不要写递归,不然会造成死循环。可以放一些比setTimeout优先执行的任务
总结
同一个上下文下,MicroTask微任务会比MacroTask宏任务先运行。
浏览器是先取出一个MacroTask宏任务执行,再执行MicroTask微任务中的所有任务。Node是按照六个阶段执行,每个阶段切换时,再执行MicroTask微任务队列
同个MicroTask队列下process.tick()会优于Promise
setImmdieate()和setTimeout(),如果他们在异步i/o callback之外调用(在i/o内调用因为下一阶段为check阶段),其执行先后顺序是不确定的,需要看loop的执行前的耗时情况。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/93749.html
摘要:如果当前没有事件也没有定时器事件,则返回。相关资料关于的架构及设计思路的事件讨论了使用线程池异步运行代码。下一篇初窥事件机制的实现二中定时器的实现 在浏览器中,事件作为一个极为重要的机制,给予JavaScript响应用户操作与DOM变化的能力;在Node.js中,事件驱动模型则是其高并发能力的基础。 学习JavaScript也需要了解它的运行平台,为了更好的理解JavaScript的事...
摘要:新加了一个微任务和一个宏任务在当前执行栈的尾部下一次之前触发回调函数。阶段这个阶段主要执行一些系统操作带来的回调函数,如错误,如果尝试链接时出现错误,一些会把这个错误报告给。 JavaScript引擎又称为JavaScript解释器,是JavaScript解释为机器码的工具,分别运行在浏览器和Node中。而根据上下文的不同,Event loop也有不同的实现:其中Node使用了libu...
摘要:如果没到毫秒,那么阶段就会跳过,进入阶段,先执行的回调函数。参考文档什么是浏览器的事件循环不要混淆和浏览器中的定时器详解浏览器和不同的事件循环深入理解事件循环机制篇中的执行机制 最近对Event loop比较感兴趣,所以了解了一下。但是发现整个Event loop尽管有很多篇文章,但是没有一篇可以看完就对它所有内容都了解的文章。大部分的文章都只阐述了浏览器或者Node二者之一,没有对比...
摘要:事件触发线程主要负责将准备好的事件交给引擎线程执行。它将不同的任务分配给不同的线程,形成一个事件循环,以异步的方式将任务的执行结果返回给引擎。 Fundebug经作者浪里行舟授权首发,未经同意请勿转载。 前言 本文我们将会介绍 JS 实现异步的原理,并且了解了在浏览器和 Node 中 Event Loop 其实是不相同的。 一、线程与进程 1. 概念 我们经常说 JS 是单线程执行的,...
摘要:前言前几天在理解的事件环机制中引发了我对浏览器里的好奇。接下来理解浏览器中的,先看一张图堆和栈堆是用户主动请求而划分出来的内存区域,比如你,就是将一个对象存入堆中,可以理解为存对象。废话不多说,直接上图个人理解。参考资料运行机制详解再谈 前言 前几天在理解node的事件环机制中引发了我对浏览器里Event Loop的好奇。我们都知道javascript是单线程的,任务是需要一个一个按顺...
阅读 1671·2021-10-09 09:44
阅读 3241·2021-09-27 13:36
阅读 1487·2021-09-22 15:33
阅读 1256·2021-09-22 15:23
阅读 1146·2021-09-06 15:02
阅读 1657·2019-08-29 16:14
阅读 2879·2019-08-29 15:26
阅读 2386·2019-08-28 18:08