Event Loop即事件循环,是解决javaScript单线程运行阻塞的一种机制。 主要是为了协调单线程下,事件、用户交互、脚本、UI 渲染和网络处理等行为,防止主线程的不阻塞。
因为JavaScript 是单线程,也就是说, 所有任务需要排队,前一个任务结束,才会执行后一个任务。
但是IO设备(输入、出设备)可能会因为网络等因数导致速度很慢(比如Ajax)继而CPU没有充分利用,所以设计者将IO设备的任务挂起,先执行后面的任务,等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。于是,就把所有任务分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。
只有前一个任务执行完毕,才能执行后一个任务;直接在主线程上排队执行且最先执行,形成一个执行栈
不进入主线程、而是进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",执行一个宏任务, 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中 宏任务执行完毕后,再依次执行执行当前微任务队列中的所有微任务,当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)
(4)主线程不断重复上面的第三步。
"任务队列"是一个先进先出的数据结构,也是一个事件的队列(也可以理解成消息的队列),IO设备完成一项任务,就在"任务队列"中添加一个事件,表示相关的异步任务可以进入"执行栈"了。主线程读取"任务队列",就是读取里面有哪些事件。
"任务队列"中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(比如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入"任务队列",等待主线程读取。
所谓"回调函数"(callback),就是那些会被主线程挂起来的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。
例子1
例子2:
例子3:
nodejs事件循环和浏览器的事件循环不一样的。浏览器的Event loop是在HTML5中定义的规范,而node中则由libuv库实现
Node JS是单线程应用程序,但它通过事件和回调概念,支持并发。 由于NodeJS每一个API是异步的,作为一个单独的线程,它使用异步函数调用,以保持并发性。Node
JS使用观察者模式。Node线程保持一个事件循环,每当任何任务得到完成,它触发这标志着该事件侦听器函数执行相应的事件。
事件驱动编程
Node.js大量使用事件,这也是为何Node.js是相当快相对于其他类似的技术。当Node启动其服务器,它可以简单地启动它的变量,声明的函数,然后简单地等待发生的事件。
在事件驱动的应用中,通常主循环监听事件,然后触发回调函数时被检测到这些事件之一。
尽管事件似乎类似于回调。不同之处在于如下事实,当异步函数返回其结果的回调函数被调用的地方作为对观察者模式的事件处理。
监听事件的功能作为观察员。每当一个事件被触发,它的监听函数就开始执行。Node.js具有多个内置通过事件模块和用于将事件绑定和事件侦听,如下
EventEmitter类可用事件:
// Import events module
var events = require('events')
// Create an eventEmitter object
var eventEmitter = new events.EventEmitter()
以下为事件处理程序绑定使用事件的语法:
// Bind event and even handler as follows
eventEmitter.on('eventName', eventHandler)
我们可以通过编程触发一个事件,如下所示:
// Fire an event
eventEmitter.emit('eventName')
例子
创建一个名为具有以下代码main.js一个js文件:
// Import events module
var events = require('events')
// Create an eventEmitter object
var eventEmitter = new events.EventEmitter()
// Create an event handler as follows
var connectHandler = function connected() {
console.log('connection succesful.')
// Fire the data_received event
eventEmitter.emit('data_received')
}
// Bind the connection event with the handler
eventEmitter.on('connection', connectHandler)
// Bind the data_received event with the anonymous function
eventEmitter.on('data_received', function(){
console.log('data received succesfully.')
})
// Fire the connection event
eventEmitter.emit('connection')
console.log("Program Ended.")
现在让我们试着运行上面的程序作为检查的输出:
$ mnode main.js
这将产生以下结果:
connection succesful.
data received succesfully.
Program Ended.