参考链接 深入浅出node.js 菜鸟教程
作者:轻酌~浅醉(建议大家阅读朴灵的深入浅出node.js,我这是抄他的,只加了很少很少的个人理解)
- 简单的说 Node.js 就是运行在服务端的 JavaScript。
- Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。
- Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。
- Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。
- 区别在于它将前端中广泛运用的思想迁移到了服务器端。
详解 异步i/o
关于异步I/O,向前端工程师解释起来或许会容易一些,因为发起Ajax调用对于前端工程师而言是再熟悉不过的场景了。下面的代码用于发起一个Ajax请求:
$.post('/url', {title: '深入浅出Node.js'}, function (data) {
console.log('收到响应');
});
console.log('发送Ajax结束');
熟悉异步的用户必然知道,“收到响应”是在“发送Ajax结束”之后输出的。在调用$.post()后,后续代码是被立即执行的,而“收到响应”的执行时间是不被预期的。我们只知道它将在这个异步请求结束后执行,但并不知道具体的时间点。异步调用中对于结果值的捕获是符合“Don’t call me, I will call you
”(不要打电话给我,我会打电话给你)的原则的,这也是注重结果,不关心过程的一种表现。
下图是一个经典的Ajax调用。
在Node中,异步I/O也很常见。以读取文件为例,我们可以看到它与前端Ajax调用的方式是极其类似的:
var fs = require('fs');
fs.readFile('/path', function (err, file) {
console.log('读取文件完成')
});
console.log('发起读取文件');
这里的“发起读取文件”是在“读取文件完成”之前输出的。同样,“读取文件完成”的执行也取决于读取文件的异步调用何时结束。
在Node中,绝大多数的操作都以异步的方式进行调用。 从文件读取到网络请求等,均是如此。这样的意义在于,在Node中,我们可以从语言层面很自然地进行并行I/O操作。每个调用之间无须等待之前的I/O调用结束。在编程模型上可以 极大提升效率。
fs.readFile('/path1', function (err, file) {
console.log('读取文件1完成');
});
fs.readFile('/path2', function (err, file) {
console.log('读取文件2完成');
});
而对于同步I/O而言,它们的耗时是两个任务的耗时之和。这里异步带来的优势是显而易见的。(因为要等上一个执行完才执行下一个)
事件与回调函数
随着Web 2.0时代的到来,JavaScript在前端担任了更多的职责,事件也得到了广泛的应用。Node不像Rhino那样受Java的影响很大,而是 将前端浏览器中应用广泛且成熟的事件引入后端,配合异步I/O,将事件点暴露给业务逻辑。
下面的例子展示的是Ajax异步提交的服务器端处理过程。Node创建一个Web服务器,并侦听8080端口。对于服务器,我们为其绑定了request事件,对于请求对象,我们为其绑定了data事件和end事件:
var http = require('http');
var querystring = require('querystring');
// 侦听服务器的request事件
http.createServer(function (req, res) {
var postData = '';
req.setEncoding('utf8');
// 侦听请求的data事件
req.on('data', function (chunk) {
postData += chunk;
});
// 侦听请求的end事件
req.on('end', function () {
res.end(postData);
});
}).listen(8080);
console.log('服务器启动完成');
require(‘http’) 为node中提供了http模块,其中封装了高效的http服务器和http客户端
- http.server是一个基于事件的HTTP服务器,内部是由c++实现的,接口由JavaScript封装
- http.request是一个HTTP客户端工具。用户向服务器发送数据。
require(‘querystring’) 模块用于处理query字符串,包含以下方法:
- parse、decode
- escape
- unescape
- encode、stringify
侦听服务器的request事件中的回调函数**(当客户端请求到来的时候,该事件被触发)**中的参数request,response,分别表示请求和响应的信息。 简单粗暴一点可以这样理解:第一个参数用来接收请求,第二个参数用来发送响应。
相应地,我们在前端为Ajax请求绑定了success事件,在发出请求后,只需关心请求成功时执行相应的业务逻辑即可,相关代码如下:
$.ajax({
'url': '/url',
'method': 'POST',
'data': {},
'success': function (data) {
// success事件
}
});
相比之下,无论在前端还是后端,事件都是常用的。对于其他语言来说,这种俯拾皆是JavaScript的熟悉感觉是基本不会出现的。
事件的编程方式具有轻量级、松耦合、只关注事务点等优势,但是在多个异步任务的场景下,事件与事件之间各自独立,如何协作是一个问题。
从前面可以看到,回调函数无处不在。这是因为在JavaScript中,我们将函数作为第一等公民来对待,可以将函数作为对象传递给方法作为实参进行调用。
与其他的Web后端编程语言相比,Node除了异步和事件外,回调函数是一大特色。 纵观下来,回调函数也是最好的接受异步调用返回数据的方式。但是这种编程方式对于很多习惯同步思路编程的人来说,也许是十分不习惯的。代码的编写顺序与执行顺序并无关系,这对他们可能造成阅读上的障碍。在流程控制方面,因为穿插了异步方法和回调函数,与常规的同步方式相比,变得不那么一目了然了。
在转变为异步编程思维后,通过对业务的划分和对事件的提炼,在流程控制方面处理业务的复杂度与同步方式实际上是一致的。
单线程
- 单线程就是一心一意,用情专一的痴情少年
- 单线程就是进程只有一个线程
- 多线程就是进程有多个线程
Node保持了JavaScript在浏览器中单线程的特点。而且在Node中,JavaScript与其余线程是无法共享任何状态的。单线程的最大好处是不用像多线程编程那样处处在意状态的同步问题,这里没有死锁的存在,也没有线程上下文交换所带来的性能上的开销。
同样,单线程也有它自身的弱点,这些弱点是学习Node的过程中必须要面对的。积极面对这些弱点,可以享受到Node带来的好处,也能避免潜在的问题,使其得以高效利用。单线程的弱点具体有以下3方面。
• 无法利用多核CPU。
• 错误会引起整个应用退出,应用的健壮性值得考验。
• 大量计算占用CPU导致无法继续调用异步I/O。
像浏览器中JavaScript与UI共用一个线程一样,JavaScript长时间执行会导致UI的渲染和响应被中断。在Node中,长时间的CPU占用也会导致后续的异步I/O发不出调用,已完成的异步I/O的回调函数也会得不到及时执行。(不用在意,没有遇到过)
跨平台
起初,Node只可以在Linux平台上运行。如果想在Windows平台上学习和使用Node,则必须通过Cygwin或者MinGW。随着Node的发展,微软注意到了它的存在,并投入了一个团队帮助Node实现Windows平台的兼容,在v0.6.0版本发布时,Node已经能够直接在Windows平台上运行了。目前我们的node.js在mac系统上、Windows上面和Linux这些上面,都可以运行,它已经开发除了对应的运行环境了。