事件循环
事件循环是一个典型的生产者/消费者模式,网络请求,异步IO源源不断的产生提供不同类型的事件到观察者哪里,事件循环然后从观察者哪里去取出事件并处理。
windows 实现方式 IOCP, linux实现方式多线程
启动进程,node会创建一个类似while的循环,每执行一次循环过程称为Tick, Tick的流程如下:
-
观察者
事件循环中有一个或多个观察者,而判断是否有事件需要处理就需要询问这些观察者。
- 浏览器中事件产生于 点击操作,资源加载等
- node中的事件产生 网络请求,异步IO操作
-
请求对象
对node而言回调函数并非由开发者来调用,从我们执行javascript后到回调函数执行中间发生了什么?
这部分中间层操作,我们称为“请求对象”。
请求对象包括:
请求对象的处理过程,下边以fs.open来说明请求对象那个的执行过程:
js fs.open(path,flag,mode,callback);
组装请求对象
fs.open()的作用是根据指定路径和参数打开文件,从而得到一个文件描述符,这是后续I/O操作的初始操作,在js执行过程中会调用C++核心模块进行下层操作。
调用流程如下:
在uv_fs_open方法调用上,我们创建了FSReqWrap请求对象那个,把js中,path,flags,mode,callback这些参数添加到FSReqWrap请求对象上。然后将callback回调函数设置到onComplete_sym属性上。到处我们的请求对象组装完成。然后在windows下调用QueueUserWorkItem()方法将FSReqWrap对象推入线程池中等待。当有可用线程时执行对应操作,js异步调用第一阶段到此结束。js不受线程池中操作影响,继续按顺序执行本身的任务。
执行回调
组装好请求对象那个,送入I/O线程池等待,在线程池中对应I/O操作完成,会将存储结果存在req->result属性上,然后调用PostQueuedCompletionStatus()通知IOCP告知当前对象操作完成,并将线程归还给线程池。在这个过程中还动用了事件循环的I/O观察者,在每次Tick的执行中,它会调用GetQueuedCompletionStatus()方法检测线程池中是否有执行完的请求,如果存在会将请求对象加到I/O观察队列,然后当做事件处理。I/O观察者从请求对象属性oncomplete_sym中获取回调函数并执行。
因此整个事件循环的流转图如下: