Node.js 读写文件
Node.js 是一个异步事件驱动的 JavaScript 运行环境,读写文件有同步和异步区别。在使用fs模块之前,需要有回调函数、promise对象的基础知识。
回调函数
在 JavaScript 中,回调函数具体的定义为:函数 A 作为参数(函数引用)传递到另一个函数 B 中,并且函数 B 运行完成后再执行函数 A。我们就把函数 A 叫做回调函数。例如:
function doSomething(msg, callback) { alert(msg); if (typeof callback == "function") callback();};
doSomething("存 5000 块", function () { alert("稍等,马上办理"); alert('2 分钟后,您的业务已办理完毕');});
回调与同步、异步并没有直接的联系,回调只是一种实现方式,既可以有同步回调,也可以有异步回调。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用,用于对该事件或条件进行响应。
同步与异步的区别
下面这段代码的执行结果,就能看出不同区别:
function doSomething(msg, callback) { console.log(msg); if (typeof callback == "function") callback();};
doSomething("存 5000 块", function () { console.log("稍等,马上办理"); setTimeout(function () { console.log('2 分钟后,您的业务已办理完毕'); }, 2000); // console.log('2 分钟后,您的业务已办理完毕');});
setTimeout(function () { console.log('5'); }, 5000);console.log('1');function test() { setTimeout(function () { console.log('2'); }, 1000);}test();
console.log('3');setTimeout(function () { console.log('4'); }, 2000);
与过程式编程相比,异步对变成带来的一些变化:因为有很多情况下是代码要求先前的代码执行完毕,如要调用之前处理的数据结果、和数据库交互等。Node.js 中可以采用回调方式解决这个问题。
使用fs模块读取文件
有同步、异步和promise对象三种方式,推荐使用promise对象。
先看一个案例:
var fs = require('fs');fs.readFile('./files/1.txt', 'utf8', function (err, data) { if (err) { throw err; } console.log(data);});
fs.readFile('./files/2.txt', 'utf8', function (err, data) { if (err) { throw err; } console.log(data);});
fs.readFile('./files/3.txt', 'utf8', function (err, data) { if (err) { throw err; } console.log(data);});
上述代码并不能保证按照1、2、3的顺序输出结果,会按照实际读取的结果展示。因为他们都是异步操作,不知道谁先读取完。 这得取决于异步任务IO的耗时。
为了确保按照顺序执行,一开始,人们的解决方案是在回调函数中,使用回调函数。代码如下:
fs.readFile('./files/1.txt', 'utf8', function (err, data) { if (err) { throw err; } else { console.log(data); fs.readFile('./files/2.txt', 'utf8', function (err, data) { if (err) { throw err; } else { console.log(data); fs.readFile('./files/3.txt', 'utf8', function (err, data) { if (err) { throw err; } console.log(data); }); } }); }});
以上按照顺序执行多个异步任务产生的问题:回调地狱问题(层层包裹进行回调,代码也不够优雅)
在ES6中,提供了promise对象来解决上述问题。https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise
promise对象
Promise
对象是由关键字 new
及其构造函数来创建的。该构造函数会把一个叫做“处理器函数”(executor function)的函数作为它的参数。这个“处理器函数”接受两个函数——resolve
和 reject
——作为其参数。当异步任务顺利完成且返回结果值时,会调用 resolve
函数;而当异步任务失败且返回失败原因(通常是一个错误对象)时,会调用reject
函数。
new Promise
返回的是一个promise
对象,这个对象有一个方法叫做then
,在其原型对象上通过这then
方法可以指定成功和失败的回调函数。
语法:promise.then(successCallback,errorCallback);
promise.then(function (data) { //then第一个函数是成功的回调,参数是resolve(err)中的data console.log('成功:' + data); // 若成功,运行结果:成功:111}).catch(function(err){ //then第二参数错误回调换成这里catch也行,两者选其一 console.log('err');}).finally(function(){ //无论失败成功都会执行 console.log('完成');})
例如,可以自定义promise
方式读取文件:
function readFilePromise(path, encoding = "utf8") { const promise = new Promise((resolve, reject) => { fs.readFile(path, (err, data) => { if (err) return reject(err); return resolve(data.toString(encoding)); }); }); return promise;}
readFilePromise("./package.json").then(res => console.log(res));
在 Node.js v12 中,引入了 fs Promise api。它们返回 Promise 对象而不是使用回调。 API 可通过 require('fs').promises
访问。
const fsPromises = require("fs").promises;fsPromises.readFile("./package.json", {encoding: "utf8", flag: "r"}) .then(console.log) .catch(console.error);