事件循环、请求对象、观察者、I/O线程池node异步I/O模型的基本要素。
比如遇到fs.open这个异步IO操作的时候,fs是内置模块,源码是通过binding调用了C++的方法,C++再调操作系统来完成操作,c++对应的操作种创建了FSReqWrap对象,从js层传过来的参数和当前方法都被封装在这个FSReqWrap对象,fs.open('url',callback)这个回调函数则被封装在FSReqWrap对象oncomplete_sym属性上。
对象封装完毕后,在window环境下,则调用了QueueUserWorkItem()方法将FSReqWrap对象推入到I/O线程池中等待。当前js层面发起的异步I/O第一阶段就结束了,js线程可以继续执行接下来的代码,当前的I/O操作在线程池中等待,不管他是否阻塞都不会影响js线程后续代码的执行,如此就达到了异步的目的。
那么异步I/O第二阶段就是执行回调了。
线程池中I/O操作完成后,会将结果存储到req的result属性上,然后调用PostQueueCompleteStatus()通知IOCP,向IOCP提交执行状态并且将线程归还线程池,通过PostQueueCompleteStatus()提交的状态,可以通过GetQueuedCompleteStatus()获取。
与此同时,事件循环的I/O观察者,在每次Tick(循环)的时候都会调用上面提到的GetQueuedCompleteStatus()方法检查线程池中是否有执行完的任务,如果有I/O观察者回调就是:将这个对象(FSReqWrap对象)的oncomplete_sym属性的回调函数作为方法,req.result返回的数据作为参数,然后执行调用,这样就是为什么fs.open的回调函数会延迟(异步)调用。
参考:深入浅出Node.js (主要就是把要点总结下,方便后续使用,更详细的可以查阅相关书籍)