node.js入门教程迷你书、node.js入门web应用开发完全(9)

function start(route, handle) {
  function onRequest(request, response) {
    var pathname = url.parse(request.url).pathname;
    console.log("Request for " + pathname + " received.");

response.writeHead(200, {"Content-Type": "text/plain"});
    var content = route(handle, pathname)
    response.write(content);
    response.end();
  }

http.createServer(onRequest).listen(8888);
  console.log("Server has started.");
}

exports.start = start;


如果我们运行重构后的应用,一切都会工作的很好:请求:8888/start,浏览器会输出“Hello Start”,请求:8888/upload会输出“Hello Upload”,而请求:8888/foo 会输出“404 Not found”。

好,那么问题在哪里呢?简单的说就是: 当未来有请求处理程序需要进行非阻塞的操作的时候,我们的应用就“挂”了。

没理解?没关系,下面就来详细解释下。

阻塞与非阻塞

正如此前所提到的,当在请求处理程序中包括非阻塞操作时就会出问题。但是,在说这之前,我们先来看看什么是阻塞操作。

我不想去解释“阻塞”和“非阻塞”的具体含义,我们直接来看,当在请求处理程序中加入阻塞操作时会发生什么。

这里,我们来修改下start请求处理程序,我们让它等待10秒以后再返回“Hello Start”。因为,JavaScript中没有类似sleep()这样的操作,所以这里只能够来点小Hack来模拟实现。

让我们将requestHandlers.js修改成如下形式:

复制代码 代码如下:


function start() {
  console.log("Request handler 'start' was called.");

function sleep(milliSeconds) {
    var startTime = new Date().getTime();
    while (new Date().getTime() < startTime + milliSeconds);
  }

sleep(10000);
  return "Hello Start";
}

function upload() {
  console.log("Request handler 'upload' was called.");
  return "Hello Upload";
}

exports.start = start;
exports.upload = upload;


上述代码中,当函数start()被调用的时候,Node.js会先等待10秒,之后才会返回“Hello Start”。当调用upload()的时候,会和此前一样立即返回。

(当然了,这里只是模拟休眠10秒,实际场景中,这样的阻塞操作有很多,比方说一些长时间的计算操作等。)

接下来就让我们来看看,我们的改动带来了哪些变化。

如往常一样,我们先要重启下服务器。为了看到效果,我们要进行一些相对复杂的操作(跟着我一起做): 首先,打开两个浏览器窗口或者标签页。在第一个浏览器窗口的地址栏中输入:8888/start, 但是先不要打开它!

在第二个浏览器窗口的地址栏中输入:8888/upload, 同样的,先不要打开它!

接下来,做如下操作:在第一个窗口中(“/start”)按下回车,然后快速切换到第二个窗口中(“/upload”)按下回车。

注意,发生了什么: /start URL加载花了10秒,这和我们预期的一样。但是,/upload URL居然也花了10秒,而它在对应的请求处理程序中并没有类似于sleep()这样的操作!

这到底是为什么呢?原因就是start()包含了阻塞操作。形象的说就是“它阻塞了所有其他的处理工作”。

这显然是个问题,因为Node一向是这样来标榜自己的:“在node中除了代码,所有一切都是并行执行的”。

这句话的意思是说,Node.js可以在不新增额外线程的情况下,依然可以对任务进行并行处理 —— Node.js是单线程的。它通过事件轮询(event loop)来实现并行操作,对此,我们应该要充分利用这一点 —— 尽可能的避免阻塞操作,取而代之,多使用非阻塞操作。

然而,要用非阻塞操作,我们需要使用回调,通过将函数作为参数传递给其他需要花时间做处理的函数(比方说,休眠10秒,或者查询数据库,又或者是进行大量的计算)。

对于Node.js来说,它是这样处理的:“嘿,probablyExpensiveFunction()(译者注:这里指的就是需要花时间处理的函数),你继续处理你的事情,我(Node.js线程)先不等你了,我继续去处理你后面的代码,请你提供一个callbackFunction(),等你处理完之后我会去调用该回调函数的,谢谢!”

(如果想要了解更多关于事件轮询细节,可以阅读Mixu的博文——理解node.js的事件轮询。)

接下来,我们会介绍一种错误的使用非阻塞操作的方式。

和上次一样,我们通过修改我们的应用来暴露问题。

这次我们还是拿start请求处理程序来“开刀”。将其修改成如下形式:

复制代码 代码如下:


var exec = require("child_process").exec;

function start() {
  console.log("Request handler 'start' was called.");
  var content = "empty";

exec("ls -lah", function (error, stdout, stderr) {
    content = stdout;
  });

return content;
}

function upload() {
  console.log("Request handler 'upload' was called.");
  return "Hello Upload";
}

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wdywdw.html