javascript點(diǎn)擊事件:EventLoop事件循環(huán)
同步模式异步模式
想要了解事件循环,我们首先要说明白JavaScript的同步模式和异步模式。
众所周知,目前主流的javaScript环境,都是以单线程的模式去执行的javaScript代码,那javaScript采用单线程工作的原因与他最早的设计初衷有关。
最早javaScript这门语言就是一门运行在浏览器端的脚本语言,那他的目的是为了实现页面上的动态交互。
而实现页面交互的核心就是dom操作,那这也就决定了,他必须使用单线程模型,否则就会出现很复杂的线程同步问题。
我们可以设想一下,假定我们在javaScript中同时有多个线程一起工作,那其中一个线程修改了某一个dom元素,而另外一个线程同时又删除了这个元素,那此时我们的浏览器就无法明确,改以哪一个线程的工作结果为准。
所以说为了避免这种线程同步的问题,从一开始javaScript就被设计成了单线程模式工作,那这也就成为了这门语言最为核心的特性之一。
那这里所说的单线程指的就是,在js的执行环境当中,负责执行代码的线程只有一个。
那你可以想象成,在我们的内部只有一个人按照我们的代码去执行任务。那只有一个人,他同时也就只能执行一个任务,那如果说有多个任务的话就必须要排队,然后一个一个依次去完成。
那这种模式他最大的优点就是,更安全,更简单,那缺点也同样很明显,如果说我们遇到一个特别耗时的任务,那后面的这些任务呢,都必须要去排队,等待这个任务的结束。
console.log('foo');for (let i = 0; i < 100000; i++) { console.log('耗时操作'); }console.log('等待耗时操作结束');复制代码
那这也就会导致我们整个程序的执行会被拖延,出现假死的情况。
那为了解决耗时任务阻塞执行的这种问题,javaScript语言将任务的执行模式分成了两种。分别是同步模式(Synchronous)和异步模式(Asynchronous)。
这里我们就了解了JS在执行的时候是分为同步任务和异步任务。上面的循环例子并不准确,一般我们的异步任务指的都是ajax请求或者定时器。
事件循环
在事件循环中有两个比较重要的概念,分别叫做宏任务和微任务。宏任务和微任务都是指代异步任务。
我们都知道JavaScript是自上而下执行的,在执行过程中涉及到执行栈和任务队列两个东西。执行中的代码会放在执行栈中执行,宏任务和微任务会放在任务队列中等待执行。
比如下面的一段代码,js自上而下执行,首先声明变量name并且赋值为yd,然后执行setTimeout定时器,由于setTimeout是一个异步任务,所以setTimeout中的函数会延时执行,这里就会将这个定时器中的函数放入到任务队列中等待1s。
代码继续向下执行, 打印出name的值,由于此时异步函数还没有执行,所以打印出来的值仍然是yd。
1s之后,浏览器中挂载的定时器到了执行时机并且开始触发,就会将任务队列中的setTimeout中的函数放入到执行栈中执行name='zd'操作。
let name = 'yd';setTimeout(function() { name = 'zd'; }, 1000);console.log(name);复制代码
上面代码的执行机制比较简单,js首先自上而下执行,当遇到异步任务会将任务加入到任务队列当中,等到当前js栈执行完毕,再去检查任务队列中是否存在可以被执行的任务,如果存在就把任务从队列中取出来放入到执行栈中执行。
宏任务
浏览器为了能够使js的内部task与DOM任务有序的执行,会在前一个task执行完毕结束后,在下一个task执行开始前,对页面进行重新渲染(render),这里说的task就是指宏任务。
task -> rander -> task复制代码
浏览器中宏任务一般包括:
setTimeout, setInterval
定时器大家都知道他的作用和用法,这里就不举例了。
MessageChannel
消息通道, 兼容性不太好,实例如下。
const channel = new MessageChannel();// 在端口号上添加消息, 发送消息channel.port1.postMessage('我爱你');// 注册接收事件, 后绑定的接收函数,还是可以接收的到,所以可以看出是异步执行channel.post2.onmessage = function(e) { console.log(e.data); };console.log('hello'); // 先走的hello,后出现我爱你.复制代码
postMessage
消息通信机制,也不过多介绍了。
setImmediate
立即执行定时器,不可以设置时间, 只在IE浏览器中实现了。
setImmediate(function() { console.log('立即执行定时器,不可以设置时间') })复制代码
以上几种就是常见的宏任务,其实宏任务中还包含点击事件等机制。
微任务
微任务通常来说就是在当前task执行结束后立即执行的任务,比如对一系列动作做出反馈,或者是需要异步的执行任务但是又不需要分配一个新的task,这样便可以减小一点性能的开销。
只要执行栈中没有其他JS代码正在执行或者每个宏任务执行完,微任务队列会立即执行。
如果在微任务执行期间微任务队列中加入了新的微任务,就会把这个新的微任务加入到队列的尾部,之后也会被执行。
微任务包括:
promise.then,
Promise的then方法就是一个微任务。
async await。
async函数的await之后的内容也是以微任务的形式来执行。
MutationObserver
MutationObserver的作用是监控dom变化,dom变化了就会执行, 时间节点是等待所有代码都执行完,才执行该监控
const observer = new MutationObserver(() => { console.log('节点已经更新'); console.log(document.getElementById('app').children.length); }); observer.observe(document.getElementById('app'), { 'childList': true, });for (let i = 0; i < 20; i++) { document.getElementById('app').appendChild(document.createElement('p')); }for (let i = 0; i < 20; i++) { document.getElementById('app').appendChild(document.createElement('span')); }复制代码
EventLoop
我们通过下面代码的执行顺序来说明白事件循环。
setTimeout(() => { console.log('timeout'); }, 0);Promise.resolve().then(data => { console.log('then'); });console.log('start');复制代码
共同學(xué)習(xí),寫下你的評論
評論加載中...
作者其他優(yōu)質(zhì)文章