我正在参加「掘金·启航计划」
常说JavaScript的事件循环,其实JavaScript没有事件循环机制。正确的说法是宿主环境中的事件循环。
一、宿主环境
字面意思:比如: 鱼,必须生活在水里, 离开了水就活不下去。 js代码就是必须在宿主环境里面才能运行,离开了宿主js代码就运行不了 。
JavaScript的宿主环境有两种:浏览器、Nodejs
如果有人问你 “说一说JavaScript的事件循环” 那咱可先有的唠了。因为事件循环并不是针对JavaScript的,JavaScript中压根没有事件循环。 只有在宿主环境中才会有事件循环的问题。 所以说大概率是想问你 “你来说一说宿主环境中的事件循环”。
二、事件循环的意义
JavaScript语言最大的特点就是单线程,简单的来说就是:"同一时间,只能做一件事情",如果有很多事情同时需要执行的话,就需要排队。
比如:在DOM中同时操作两个线程,一个线程用来删除某个节点上的元素;另外一个线程用来在这个线程上添加元素。浏览器应该先执行哪个?
所以需要事件循环机制。只有等删除操作的线程执行完毕之后,下一个线程才能执行。但是如果一个线程的执行时间太长的话,就会造成拥堵。事件循环机制就是为了来解决这个事情的。
事件循环 = 事件 + 循环
**事件:**js中的事件类型,如: 点击事件,鼠标移入移出事件,聚焦事件,失去焦点事件等等。
**循环:**一直重复的做,js中的while循环,for循环。
浏览器的事件循环
执行js代码
请求数据,接收响应数据
解析css
渲染页面
鼠标的点击、移入移出等等事件
在浏览器的内部有很多的模块,每一个模块后面的其实就是一个进程,也就是说一个模块就是一个进程。
1.1、进程、线程
**主进程:**用来协调控制其他子进程。
**GPU进程:**用于3D绘制等。 渲染进程:就是我们说的浏览器内核,负责具体页面的渲染,脚本执行,事件处理等。
浏览器的每个tab页背后就有一个渲染进程。 进程这个单位还是比较大,它进一步拆分多个线程。可以理解为一个页面上的事还是比较多,要多找些小弟来完成。
具体来说,一个渲染进程包括:
-
主线程: 统一调度其他线程
-
GUI渲染线程: 负责渲染页面,布局和绘制。与JS引擎互斥。
-
JS引擎线程: 负责处理解析和执行javascript脚本程序。与GUI渲染线程互斥的
-
事件触发线程: 用来控制事件循环(鼠标点击、setTimeout、ajax等)。当事件满足触发条件时,将事件放入到JS引擎所在的执行队列中。(为什么你触发的事件可以被捕捉到,是因为此线程在监听)
-
setInterval与setTimeout所在的线程: 定时任务并不是由JS引擎计时的,是由定时触发线程来计时的。计时完毕后,通知事件触发线程。(其实定时器并不是ECMA所规定的的,而是宿主环境给予的)
-
异步http请求线程: 浏览器有一个单独的线程用于处理AJAX请求,当请求完成时,若有回调函数,通知事件触发线程。
-
io线程: 用来接收其他进程的消息。
每个渲染进程(一个tab页)都有一个主线程,并且主线程非常繁忙,既要处理 DOM,又要计算样式,还要处理布局,同时还需要处理 JavaScript 任务以及各种输入事件(但是如果在JS引擎线程里面写一个死循环的话,后面的事情都得堵着)。
浏览器又是如何的去有条理的去处理这些不同的类型的任务在主线程里面执行,这个时候就需要一个系统来调度它们,指挥它们。这个调度的系统就是消息队列和事件循环.
1.2、消息队列-事件循环
任务有很多,人只有一个,且任意时刻只能做一件事(不是一边走路一边听课这种事哈),那怎么办,排队呗。
简单的理解就是一个教室只有一个老师,但是会有很多的学生,学生们同时的向老师提出不同的问题,老师是不可能一下全部的解决掉的,而是拿一个小纸条放在讲台上,学生们把自己的问题写在小纸条上面;老师解决了当前学生的问题就会优先的解决下一个学生的问题,当所有学生的问题全部被解决完毕之后,老师也就闲下来了; 如果哪个学生还有问题就举手,老师就再去解决,老师就这样一直循环着,有问题就解决; 没有问题就等待着问题,再去解决问题。
消息队列就好比是,同时出现问题;就得排队。先来的先做。
事件循环就好比是,同一时间没有问题了,老师闲下来了;学生一有问题老师就去解决,一直循环。
主线程上要做很多事情,例如:js代码执行,页面布局计算,渲染等
主线程同一时刻只能做一件事,事情多了就要排队。所以主线程维护了任务队列。
某个事件发生时,事件触发线程 就把对应的任务添加到主线程的任务队列中。
主线程上的任务完成之后,就会从任务队列中取出任务来执行。
最后
JS任务是以事件以及回调的方式存在的(如: onclick(回调函数))
当事件(用户的点击,图片的成功加载)发生时,IO线程就会将其回调添加到任务队列中
当主线程中的任务完成之后,就会以从任务队列中取出任务来执行
此过程不断的重复的去执行,就会形成一个循环;此循环称之为EventLoop
抛开前端,浏览器不谈,说一个更广的层面; 在程序世界里面,事件循环机制是很常见的。也就是事有很多,但是干活的只有一个,那就一定要排队;并且还有一个机制去指挥它们,而这个机制就是事件循环机制EventLoop。