相信作为前端的我们或多或少都接触过Node,它以异步非阻塞IO模型闻名,特别擅长处理IO密集型任务。Node的异步操作是通过Libuv实现的,学习它能够能够帮助我们更好的理解Node,本文是Libuv学习系列的第4篇,讲解文件处理。
前言

由于没有跨平台的异步文件处理API可以使用,Node作者通过在线程池中执行同步文件操作来实现异步,处理完将操作结果返回给主线程,并执行回调函数。具体操作如下:主线程发起一个文件操作的请求,将其封装成一个任务对象,其丢到任务队列中;线程池中空闲的线程从任务队列中获取任务并执行;执行完毕后通知主线程,主线程了解到文件操作完毕,调用用户先前设置好的回调函数。

在上篇文章《Libuv学习——线程池》中,我们已经详细介绍了主线程如何将任务提交到任务队列、线程池如何从任务队列中获取任务并执行、执行完毕后如何通知主线程,所以下面就不介绍这些操作的细节了,想要了解的同学可以去看下《Libuv学习——线程池》。
DEMO
我们从一个例子讲解Libuv的文件操作是如何处理的,代码很简单,以异步的方式打开一个文件,获取该文件的句柄。

源码分析
上面唯一需要分析的代码就是uv_fs_open,所以就让我们来看下它吧:

该函数首先调用了INIT和PATH两个宏,将其展开能看的更清楚一点,本质上就是对req对象进行属性设置:

除了调用INIT和PATH宏,后面还调用了POST这个宏,让我们详细的看下它做了什么:

这个宏也很简单,如果请求有回调函数,表明是异步处理,将文件处理封装成任务对象,提交给任务队列,线程池从任务队列中获取任务执行uv__fs_work,处理完毕后通知主线程调用uv__fs_done;如果没有回调函数,说明是同步处理,直接在主线程中调用uv__fs_work函数。
这里我们可以整理一下,异步处理时提交到任务队列的任务可以描述如下:
{
fs_type: 'UV_FS_OPEN',
path: 'xxxx',
cb: () => {...}
work: uv__fs_work,
done: uv__fs_done
}接下来,让我们看下在线程池中要执行的uv__fs_work函数:

uv__fs_work本质上就是一个大switch,根据fs_type来执行不同的操作,这里执行的是uv__fs_open,执行完将结果赋值到result属性上。这样我们的任务就变成了如下状态了:
{
fs_type: 'UV_FS_OPEN',
path: 'xxxx',
cb: () => {...}
work: uv__fs_work,
done: uv__fs_done,
result: xxx,
}文件处理在线程池中处理完毕后,会通知主线程执行uv__fs_done,让我们来看下它吧:

没什么特殊的,就是调用了一下回调函数cb。
总结
这样上面的DMEO就讲解完毕了,Libuv中其他异步文件操作和这个例子类似,这里就不张开了。非常感谢看到最后的同学,下一篇将会初步介绍Libuv(Node)的事件循环,如果有兴趣的话,欢迎关注我。
