转载

菜鸟的Node.js之旅

凌晨三点,手在键盘上,盯着空空如也的控制台。明亮的提示符在黑色背景下待命,期待着输入。想折腾一会儿 Node.js 吗?Node.js 的一大优点是可以在任何地方运行。这就带来了众多的实验性玩法。对任何一个老程序员而言,命令行工具都充满乐趣。我最喜欢的一点是,可以先从带有安全措施的命令行开始尝试。更酷的是我们谈论的其实还是 JavaScript,大多数读者对此应该不会感到陌生。现在就在控制台把 node 跑起来吧!

本文介绍 Node.js 主要目的是带读者领略一下 Node 最有趣的部分,同时对某些地方进行深入探讨。本文是中等难度的Node.js技术栈概览,所有内容都限制在控制台之内。如果你在找的是方便初学者上手的 Node.js 教程,我建议你看一下 SitePoint 提供的付费教程:Node.js 入门 。

为什么选择 Node.js?

首先,罗列一下让 Node.js 脱颖而出的几个要点。 •是非阻塞 I/O 设计 •是异步操作设计 •可以运行 Chrome V8 JavaScript

你或许已经从多种渠道获悉了以上几点,但是它们都是什么意思呢?可以把 Node.js 当作是对 JavaScript 开放了众多 API 的引擎。在传统的计算过程是同步的,也就是说进行 I/O 操作的时候,API 在运行下一行代码之前要等待。这里说的 I/O 操作可以是读取文件或者进行网络访问。而 Node.js 没有选择这样做,而是从设计之初就使用异步操作。现如今这是巨大的优势。还记得上一次是什么时候因为更快的单核处理器买了一台新电脑吗?多核和高速硬盘驱动器才是更重要的。

下文中的 > 代表命令行提示符,也就是说需要按下回车键来输入下一个命令。还要记得先打开终端输入 node 命令再执行文中的代码。那么让我们开始Node.js之旅吧。

回调函数

首先,输入以下函数: 1.> function add(a, b, callback) { var result = a + b; callback(result); }

对新手而言,JavaScript 的回调函数看上去或许有点奇怪。它当然看上去不像传统的面向对象编程的方法。对于 JavaScript 来说,函数也是对象,而对象是可以接受其他对象作为参数的。JavaScript 不关心对象有什么,所以函数可以接受一个对象,而该对象可以是另外一个函数。参数的数量可以像 add() 一样有两个,也可以像回调函数只有一个。回调函数系统功能强大,因其使得封装和隐藏实施细节成为可能。许多 Node.js 的 API 都将回调函数作为参数。还可以将回调函数看作是一种「代理」。绕过编程术语不谈,「代理」就是被派遣并被授权代表其他人的人。这么看来,回调函数就像派人跑腿。只要给定一个参数列表,比如一个购物清单,「代理」就可以自行完成任务。

再尝试一下add: 1.> add(2, 3, function (c) { console.log('2 + 3 = ' + c) }); 2.> add(1, 1, function (c) { console.log('Is 1 + 1 = 3? ' + (c === 3)); });

你可以随便试试用更具创造性的方式使用回调函数。回调函数是一些重要的 Node.js API 的基石。

异步操作

有了回调函数就可以构建异步 API 了。举个例子: 1.> function doSomething (asyncCallback) { asyncCallback(); } 2.> doSomething(function () { console.log('This runs synchronously.'); });

这个特殊的例子可以同步执行。但是却有了在 JavaScript 实现异步操作的全部要素。比如说 asyncCallback ,就可以在同一个线程中延迟。 1.> function doSomething (asyncCallback) { setTimeout(asyncCallback, Math.random() + 1000); } 2.> doSomething(function () { console.log('This runs asynchronously.'); }); console.log('test');

使用 setTimeout 在当前线程实现延迟执行。超时不能保证执行的时间。加入 Math.random() 可以使之更随机。随后调用 doSomething() ,再用 console.log('test') 显示延迟的执行。你会感受到大概一到两秒钟的短暂延时,随后会有消息显示在屏幕上。这个例子说明,异步回调是不可预测的。Node.js 将回调函数交给调度程序之后就不再管它,然后自己该干嘛干嘛去了。一旦计时器到时间了,Node.js 就会回到执行发生的地方并调用回调函数。所以只有想方设法地理解了回调函数才能理解 Node.js。

总之,Node.js 的回调函数并不总是和 JavaScript 一致。

再来看一些更酷的例子。如何用 Node.js 实现一个简单的 DNS 查询? 1.> dns.lookup('bing.com', function (err, address, family) { console.log(' Address: ' + address + ', Family: ' + family + ', Err: ' + err); });

回调函数的返回了 err,address 和 family 对象。重点是返回值像参数一样被传给了回调函数。因此,这与 var result = fn('bing.com'); 这种传统的 API 是不同的。对于 Node.js,要着眼大局就必须使用回调函数和异步操作。请随意查阅DNS Node.js API[2] 文档了解更多细节。下面是我在控制台上实现的 DNS 查询的结果。

文件I/O

赶紧再看一个例子,如何用 Node.js 读写文件呢?想象下面这个场景,你打开一个文件,读取内容再写一些东西进去。在现代计算机体系结构中,I/O 密集型操作相当滞后。CPU 寄存器,CPU 缓存和 RAM 的读取速度都很快。读取磁盘的速度却很慢。所以同步程序执行 I/O 密集型操作的时候会非常慢。此时最好使用异步,下面就是一个例子: 1.> var fs = require('fs'); 2.> fs.writeFile('message.txt', 'Hello Node.js', function () { console.log('Saved.'); }); console.log('Writing file...');

因为事实上操作是异步的,在文件保存在磁盘之前你会看到“写入文件......“。这个 API 中自然地使用回调函数是非常合适的。你可以查阅文件系统API[6] 文档获取更多信息。那么如何读取文件呢?你可以不假思索地猜出如何用 Node.js 实现吗?一个提示,回调函数的参数是 err 和 data 。自己先试着解一下这个问题。

下面给出答案: 1.> fs.readFile('message.txt', function(err, data) { console.log(data); });

还可以通过传入 encoding 选项来获得 utf-8 编码的文件的内容: 1.> fs.readFile('message.txt', {encoding: 'utf-8'}, function(err, data) { console.log(data); });

在 Node.js 中结合异步 I/O 使用回调函数看上去不错吧。这样做的好处在于改善了 JavaScript 的一个基本组成部分。有了非阻塞的异步 API ,回调函数得以提升到令人惊叹的新高度。

Web 服务器

那么怎样实现 Web 服务器呢?任何出色的 Node.js 示范总得通过运行 Web 服务器得以体现。假设有一个名为 createServer 的 API ,它有一个回调函数,而函数接受两个参数:request 和 response 。你可以研究一下HTTP的API文档。能想出个大概吗?这时你需要的是 http 模块。开始吧,在控制台上尝试一下。

下面是答案: 1.> var http = require('http'); 2.> var server = http.createServer(function (request, response) { response.end('Hello Node.js'); });

当你在考虑 web 的时候,其实是在想一个可以发起请求并做出应答的客户端-服务器模型。Node.js 具有来自客户端的 request 对象和来自服务器的 response 对象。所以「Node.js技术栈」用这种简单的回调机制欣然吸收了 web 的关键所在。我还需要提醒你这是异步的吗?我希望你已经能够拼凑出一个整体的印象了。如果回顾一下文件 API ,那么这里的东西没有两样。依旧是引入一个模块,交待它要去做什么,然后将其传给回调函数。回调函数就跟代理一样,会根据给定的参数猎豹执行指定的任务。

当然,如果我们不能看到它在浏览器上正常运行的话一切都是扯蛋。在命令行上输入: 1.server.listen(8080);

然后用你喜欢的浏览器去访问 localhost:8080 就好了,我用的是 Edge 浏览器,如下图所示:

不妨将 request 看作是可以给你提供海量信息的对象。若是要重新连接的 server ,先将其关闭: 1.> server.close(); 2.> server = http.createServer(function (request, response) { response.end(request.headers['user-agent']); }); server.listen(8081);

用浏览器访问 localhost:8081。headers 对象会提供从浏览器得来的 user-agent 信息。还可以循环访问 headers 对象: 1.> server.close(); 2.> server = http.createServer(function (request, response) { Object.keys(request.headers).forEach(function (key) { response.write(key + ': ' + request.headers[key] + ' '); }); response.end(); }); server.listen(8082);

此时还是用浏览器访问 localhost:8082。尝试完服务器操作之后记得将其关闭。不然命令行可能会出现一些奇怪的问题。 1.> server.close();

这样就搞定了,我们完全用命令行就构建了 Web 服务器。希望你喜欢这个围绕着 node 的奇幻之旅。

结论

Node.js 简单轻巧,非常适合用作现代化的解决方案。它通过无阻塞设计充分利用了现代化的硬件。它还包含了 Web 固有的客户端-服务器模型。最重要的是它运行的是我们喜爱的 JavaScript 。最吸引我的是,Node.js「技术栈」的核心都不是什么新玩意。Web 诞生之初就是围绕着轻量级可访问模块构建的。我建议你抽空阅读一下蒂姆·伯纳斯的设计原则这篇文章。文中的最少能量原则对 Node.js 来说就是选择采用 JavaScript。

希望你喜欢本文的命令行工具之旅,祝你 happy hacking.

原文链接: http://www.gbtags.com/gb/share/9493.htm

正文到此结束
Loading...