上一篇读书笔记中中我们讲到了异步,这里不得不提一下Promise,现在的ES6已经原生支持Promise对象。所谓的Promise对象,其实就是一个异步操作,使用Promise的好处在于,我们可以把异步操作用同步的方式来写,避免了层层嵌套的回调函数(俗称callback hell)。以下的内容主要来自陈天大大的一篇文章讲Promise的文章,然后又加上了Node.js中Promise的一种实现方法。
Promise的状态机
具体代码,这里首先连接数据库,然后打开某一个某,最后插入数据,一个三层的嵌套
用Promise改造后就变成类似同步方法的写法,其中为了实现链式调用,每一个then语句中都要返回一个Promise对象,且最后的catch语句处理之前的then语句中发生的所有异常。
下面是我自己写的一个实现了链式调用Promise且能在得到不想要的结果时退出的一段代码
<script type="text/javascript">
var getDate = function(url) {
var promise = new Promise((resolve, reject) => {
var xhr;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
xhr.onreadystatechange = handler;
xhr.open("GET", url);
xhr.send(null);
function handler() {
if (this.readyState === this.DONE) {
if (this.status === 200) {
resolve(this);
} else {
reject(this);
}
}
}
});
return promise;
}
getDate("http://127.0.0.1:3000/data").then(function (data) {
//判断结果,若不是Frontend,则直接抛出异常,跳转到最后的catch语句中
if (data.response !== "Frontend") {
throw "Error";
}
console.log('Correct');
//再次返回一个Promise对象,实现链式调用
return getDate(data.responseURL)
}).then(function (data) {
console.log(data.response);
}).catch(function (err) {
console.log(err);
});
</script>
第五章 内存控制
背景
因为Node.js处于服务器端,所以内存的问题就必须重视起来,这里与浏览器中很少会碰到内存的一些问题,很多时候当发生垃圾回收造成页面加载缓慢时,用户一般就不耐烦地刷新了页面。但是在Node.js中,面对海量请求的时候,哪怕是一丁点的内存泄漏也会越积越多,最后造成服务器的崩溃!
介绍
首先,Node.js使用V8作为执行引擎,所有它的内存管理是交给V8来实现的。Node.js中的内存分为两部分:
V8分配的内存(受内存管理的限制),在32位系统下默认为0.7GB,64位系统下默认为1.4GB,但这个限制可以在启动Node时改变。
node --max-old-space-size=2400 app.js //指定老生代内存大小,单位为MB
node --max-new-space-size=1024 app.js //指定新生代内存大小,单位为KB
Node自行分配的内存(例如Buffer对象,不受到V8内存管理的限制,在处理几个GB的大文件时特别有用)。
同时在V8中,内存被分为两部分:- 老生代(占比98%,存放存活时间较长的对象 ,应用Mark-Sweep和Mark-Compact算法)
- 新生代(占比2%,存放存活时间较短的对象,应用半空间复制算法Scavenge)
V8中一次小的垃圾回收要50ms,一次大的非增量的垃圾回收要1s以上,这是几乎无法忍受的,所以它内部通过各种算法来优化垃圾回收,算法细节有兴趣的可以自行Google。
我们可以通过分析V8执行时的日志来查看垃圾回收所占用的时间,用如下的方法来启动Node
,这样在根目录中会生成对应的日志文件,如下图所示:
对应的GC.js文件的代码
for (var i = 0; i < 1000000; i++) {
var a = {};
}
for (var j = 0; j < 1000000; j++) {
var b = {};
}
然后在安装了tick模块后,我们就可以看到垃圾回收实际产生的影响了。这里我是在WebStrom中直接用集成好了的工具来做的,省时省力。
分析结果如下图
从结果里可以看到,由于不断分配对象,垃圾回收所占的比列为1%(我的Node版本是6.2.0,已经对垃圾回收做了很多优化),这意味着事件循环每执行1000ms,就要抽出10ms的事件来执行垃圾回收。
高效使用内存
为了高效使用内存,我们在开发中要注意几点:
- 减少全局变量的使用,通过重新赋值(null或undefined)来释放全局变量,虽然删除对象也可以取得相同的效果,但删除对象有可能干扰到V8的垃圾回收机制,所以采用重新赋值的方法来解除占用会更好。
- 减少闭包的使用
- 不要把内存当做缓存,这样会干扰垃圾回收,且还要自己制定缓存的过期策略,推荐使用Redis或者Memcached。
大内存应用
在需要操作大文件时,由于V8内存的限制,不能直接通过fs.readFile()和fs.writeFile()进行操作,而是需要用到stream模块或者继承stream的模块。代码如下
var fs = require('fs');
var reader = fs.createReadStream('../../public/v8_log.log');
var writer = fs.createWriteStream('../../public/v8_log_copy.log');
reader.on('data', function (chunk) {
writer.write(chunk);
});
reader.on('end',function(){
writer.end();
});