封装Promise版本的 readFile

3,047 阅读4分钟

写在前面

作者最近遇到了一个有趣的问题,我们都知道文件读取有两种类型

fs.readFileSync(); //同步读取
fs.readFile(); //异步读取

而Promise 是异步编程的一种解决方案, 那么我们能否封装一个Promise版本的 readFile呢?

引子

我们来看看阮一峰老师关于javascript是单线程的解释

链接:www.ruanyifeng.com/blog/2014/1…

JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事,在发出一个调用时,必须等待上一个任务执行完才能执行下一个任务,这种执行模式叫做同步。单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。于是,所有任务可以分成两种,一种是同步任务,另一种是异步任务。

什么是同步编程?

所谓同步编程,就是计算机按顺序一行一行依次执行代码,当前代码任务耗时执行会阻塞后续代码的执行。

什么是异步编程?

所谓异步编程,就是在调用在发出后,这个调用就直接返回了,调用者不会立即得到结果,但是不会阻塞,可以继续执行后续的操作。用一句话来说就是

程序无须按照代码顺序自上而下的执行

需要的文件

这是我们要引入的template.html文件

<html>
    <head></head>
    <link rel="stylesheet" href="style.css">
    <body>
        <h1>不吃算拉~</h1>
        <img src="./eat.jpg" alt="" width="100px" height="100px">
        <script src="./common.js"></script>
    </body>
</html>

样式和js可以自己写,我写的有点粗糙,在本地上预览效果如下:

同步方式读取

现在我们用同步的方式读取这个文件

const Koa = require('koa'); 
const app = new Koa(); 
const static = require('koa-static');
const fs = require('fs');
const main = ctx => {
    ctx.response.type = 'html'; // 响应头
    const html = fs.readFileSync('./template.html','utf-8');    // 同步读取
    // console.log(html);
    ctx.response.body =html;
}
app.use(static('./'));
app.use(main); 
app.listen(3000);

这是同步的写法,会造成阻塞。对于高并发来说,非常消耗时间。

异步方式读取

现在我们来使用性能更高,速度更快,而且没有阻塞的异步方法。 代码如下:

const Koa = require('koa'); 
const app = new Koa(); 
const static = require('koa-static');
const fs = require('fs');
const main = async ctx => {
    ctx.response.type = 'html'; // 响应头
    // 封装Promise 版本的 readFile
    let pReadFile = function (filePath) {
        return new Promise(function (resolve, reject) {
            fs.readFile(filePath, 'utf-8', function (err, data) {
                if (err) {
                    reject(err);
                }
                resolve(data);
            });
        })
    }
    await pReadFile('./template.html').then(data => {
        ctx.response.body = data;
    })
}
app.use(static('./'));
app.use(main); // 启用了一个服务 给访问者用 Visitors 使用
app.listen(3000);

预览效果

可以看到,我们成功完成了readFile中Promise版本的封装。

对上述代码的一些解释

  • 引入了koa-static 处理静态资源
  • Promise,promise是ES6为解决异步回调而生,它通过 then 链来解决多层回调的问题,我又结合了 async/await 来进一步优化它。
  • 什么是Async/Await?
    • async/await是基于Promise实现的,它不能用于普通的回调函数。
    • async/await与Promise一样,是非阻塞的。
    • async 是“异步”的简写,而 await 可以认为是 async wait 的简写。所以应该很好理解 async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成。

总结

对于以上代码,其实我们还有更简单的方法来实现,这里用到了流(Stream)的思想

const fs = require('fs');
const Koa = require('koa');
const app = new Koa();
const static = require('koa-static');
const main = ctx =>{
    ctx.response.type = 'html';
    ctx.response.body = fs.createReadStream('./template.html');
}
app.use(static('./'));
app.use(main);
app.listen(3000,function(){
    console.log('在3000端口上启动成功')
});

可以看到, 流的使用更加强大。这里不做过多赘述。

写在后面

笔者目前还是一名在读大三学生, 如果有相应错误,欢迎大佬指正,也欢迎大家能够一起交流问题,希望能和大家一起得到成长!