Node.js基础教程学习笔记(3)

说明:本人是node.js的初学者,尝试向别人解释这是怎么回事是自我学习的一个好方法。如果你发现有些地方并不是那么正确,欢迎提出来让我知道以便修正,共同进步,谢过^_^。 

很多地方都涉及到函数的回调,在这里简单说一下什么是函数的回调。

回调函数就是回来再调用的函数。

基于js的单线程执行代码的风格,回调是必须的选择。也可以说是一种不得已而为之的选择吧,回调无疑增加了代码的复杂性,使其变得难读、难理解,难维护。但是,在解决实际问题的时候,回调又非常有效。下面举例说明:

我们要读取一个很大的文件,但是我们有不需要立刻获得读取的结果,并且,我们可不只有读取文件这个任务,我们还要执行其他的代码。这个时候,我们就需要回调函数过来帮忙。

var fs=require(“fs”);
fs.readFile(‘text.txt’,utf-8,function(err,result){
    If(err) throw err;
    //result就是读取出来的数据
});
console.log(‘下一项任务’);

这样一来,就可以把读文件那个任务放一放了,我们完成完我们的下一个任务的时候,闲得慌的时候再回来执行它不晚,有错误就抛出来。

这个方法在很多的node.js工程上面有大量的应用(就我接触的而言),复制一段项目里面的几句代码(有关查询数据库):

conn.query("select login,qq,phone,type from user where",function(err, rowss, fields) {

if (err) {console.log(err);res.end();return;}

if(rowss.length<1) {res.end("{{}}alert{}操作失败,你的手机没有登记过{{}}");return;}

这里的function就是回调函数。

我又在另一篇国外的人写的文章,他从客户端的角度阐述了为什么采取回调函数的解决方案是node的必然选择:我们知道,php为每一个客户端开辟一个新的线程用于服务新的请求,但是由于node的“先天残疾”,如果前一个请求需要花费5s,后一个请求就不得不等待5s!这是不能忍的,我在抢票回家过年好咩!!这个叫阻塞,前一个阻塞掉后一个。我们要把它变成一个非阻塞的,每个人都有公平的机会抢到回家的票。于是就用到回调函数。

文章中还打了一个很有意思的比喻,这里摘抄过来:

你在一个狭窄的道路上开车,前面有一个SB在停着打电话,很忙的样子(阻塞代码)使你不能到达目的地,这样你必须等这个SB打完电话把车启动起来才 能继续(有人可能想,用板砖干他丫的,但从程序角度来说,把他丫干死,前面少了一个司机,你要等警察来拖走或者自己先开走他的后再开自己的车,外加法律责 任,代价是很大滴,这叫破坏模型,比阻塞模型的代价还大)。

想像一下如果这条路上有紧急停车带,前面那SB司机可以变得不SB,先把车停在紧急停车带打电话。把路让出来让你先继续你的旅程。当那个不再SB的 司机打完电话之后也可以回到主干上来继续前行,还避免了可能碰到的板砖型程序员而导致血光之灾,皆大欢喜。这跟异步调用很像,在同一时间同一主干上跑多辆车。

非常有趣,isn’t it?

咳咳,好了言归正传。上代码(三段)说明问题!!

var http = require('http');

var url = require('url');

http.createServer(function (request, response) {

response.writeHead(200, {'Content-Type': 'text/plain'});

if( url.parse(request.url).pathname == '/wait' ){

var startTime = new Date().getTime();

while (new Date().getTime() < startTime + 15000);

response.write('Thanks for waiting!');

} else{

response.write('Hello!');

}

response.end();

}).listen(8080);

console.log('Server started');

代码读起来不难,创建了可一个服务器,监听8080端口。我们运行node这个文件之后,在浏览器里面输入localhost:8080/wait回车,代码开始起作用,十五秒之后蹦出来一句话。

这不能说明什么问题。但是,在你按下回车之后的15秒内,另一个客户需要访问这个这个服务器的时候,也需要等待你完成之后才轮到他。黄花菜都凉了!

下一段代码(包含两个文件):

block.js:

var startTime = new Date().getTime();

while (new Date().getTime() < startTime + 10000);

main.js:

var http = require('http');

var url = require('url');

var cp = require('child_process');

function onRequest(request, response) {

var pathname = url.parse(request.url).pathname;

if( pathname == '/wait' ){

cp.exec('node block.js', myCallback);

}

else{

response.writeHead(200, {'Content-Type': 'text/plain'});

response.write('Hello!\n');

response.end();

}

console.log('New connection');

function myCallback(){

response.writeHead(200, {'Content-Type': 'text/plain'});

response.write('Thanks for waiting!\n');

response.end();

}

}

http.createServer(onRequest).listen(8080);

console.log('Server started');

两段代码类似,这个多申请了一个cp子进程,用于在用户访问wait域名的时候调用block.js。在其他用户访问非wait域名的时候,服务器仍然能及时响应hello,这个时候道路还是通畅的,原因就是上一个sb还在紧急停车带打电话呢!这一切都归功于cp.exec('node block.js', myCallback);里面的回调函数:myCallback。.exec函数有两个参数,一个调用block.js,另外一个执行回调函数。

第三段代码(一个读取文件的程序):

var http = require('http');
var fileSystem = require('fs');
http.createServer(function (request, response) {
response.writeHead(200, {'Content-Type': 'text/plain'});
var read_stream = fileSystem.createReadStream('myfile.txt');
read_stream.on('data', writeCallback);
read_stream.on('close', closeCallback);
function writeCallback(data){
response.write(data);
}
function closeCallback(){
response.end();
}  }).listen(8080);
console.log('Server started');

这里使用了内置的文件操作模块,使用函数.createReadStream()读取文件。

总结:

无论你是否有需要执行一个耗时很长的程序与否,你都应该使用非阻塞模型,并且记住,正确使用回调和异步可以让代码的速度和稳定性都能得到提高。

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

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