Node.js学习笔记(3)——关于回调函数和函数的回调

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

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

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

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

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

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

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

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

1 conn.query("select login,qq,phone,type from user where name=‘"+myphone+"‘",function(err, rowss, fields) {
2
3          if (err) {console.log(err);res.end();return;}
4
5          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()读取文件。

总结:

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

时间: 2024-10-16 11:43:08

Node.js学习笔记(3)——关于回调函数和函数的回调的相关文章

Node.js学习笔记【1】入门(服务器JS、函数式编程、阻塞与非阻塞、回调、事件、内部和外部模块)

笔记来自<Node入门>@2011 Manuel Kiessling JavaScript与Node.js Node.js事实上既是一个运行时环境,同时又是一个库. 使用Node.js时,我们不仅仅在实现一个应用,同时还实现了整个HTTP服务器. 一个基础的HTTP服务器 server.js:一个可以工作的HTTP服务器 var http = require("http"); http.createServer(function(request, response) { r

Node.js学习笔记(3) - 简单的curd

这个算是不算完结的完结吧,前段时间也是看了好久的Node相关的东西,总想着去整理一下,可是当时也没有时间: 现在看来在整理的话,就有些混乱,自己也懒了,就没在整理,只是简单的记录一下 一.demo的简单介绍 这次demo,只涉及到简单的curd操作,用到的数据库是mongo,所以要安装mongo数据库,数据库连接驱动是mongoose: 当然关于mongo的驱动有很多,比如mongous mongoskin等:(详见http://cnodejs.org/topic/4f4ca8e0940ce2e

系列文章--Node.js学习笔记系列

Node.js学习笔记系列总索引 Nodejs学习笔记(一)--- 简介及安装Node.js开发环境 Nodejs学习笔记(二)--- 事件模块 Nodejs学习笔记(三)--- 模块 Nodejs学习笔记(四)--- 与MySQL交互(felixge/node-mysql) Nodejs学习笔记(五)--- Express安装入门与模版引擎ejs Nodejs学习笔记(六)--- Node.js + Express 构建网站预备知识 Nodejs学习笔记(七)--- Node.js + Exp

Node.js学习笔记【3】NodeJS基础、代码的组织和部署、文件操作、网络操作、进程管理、异步编程

一.表 学生表 CREATE TABLE `t_student` ( `stuNum` int(11) NOT NULL auto_increment, `stuName` varchar(20) default NULL, `birthday` date default NULL, PRIMARY KEY  (`stuNum`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 学生分数表 CREATE TABLE `t_stu_score` ( `id` int(11

node.js学习笔记目录

1.node.js学习笔记(1)--Node.js简介及环境安装 2.node.js学习笔记(2)--使用Express快速创建应用 3.node.js学习笔记(3)--Express创建的项目分析 4.node.js学习笔记(4)--使用Express完成简单的登陆 5.node.js学习笔记(5)--MongoDB下载及安装 6.node.js学习笔记(6)--MongoDB简单入门 7.node.js学习笔记(7)--Node.js与MongoDB简单交互 8.node.js学习笔记(8)

Node.js学习笔记(4)——除了HTTP(服务器和客户端)部分

很多node入门的书里面都会在介绍node特性的时候说:单线程,异步式I/O,事件驱动. Node不是一门语言,它是运行在服务器端的开发平台,官方指定语言为javascript. 阻塞和线程: 线程在执行中如果遇到磁盘读写或网络通信(统称为 I/O 操作),通常要耗费较长的时间,这时操作系统会剥夺这个线程的 CPU 控制权,使其暂停执行,全力执行这个I/O操作,同时将资源让给其他的工作线程,这种线程调度方式称为阻塞.当其他完成之后,系统再恢复它对cpu的控制权,继续执行,这就是同步I/O或者阻塞

Node.js学习笔记(一)module.exports与exports

在node.js中模块与文件是一一对应的,也就是说一个node.js文件就是一个模块.如将函数作为模块的方法导出: var printName=function(name){ console.log(name); }; exports.printName=printName; 然后在index.js文件中请求这个模块,使用该模块的方法: var name=require('./name'); name.printName(xiaomu); //xiaomu 其实module.exports才是模

node.js学习笔记(二)——回调函数

Node.js 异步编程的直接体现就是回调. 那什么是回调呢?回调指的是将一个函数作为参数传递给另一个函数,并且通常在第一个函数完成后被调用.需要指明的是,回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应.回调函数在完成任务后就会被调用,Node 使用了大量的回调函数,Node 所有 API 都支持回调函数.例如,我们可以一边读取文件,一边执行其他命令,在文件读取完成后,我们将文件内容作为回调函数的参数返回.这样在执行代码时就没有阻

node.js 学习笔记1-mocha

1.mocha是什么 mocha是一款流行的额JavaScript测试框架. 2.mocha的基本用法 1.使用npm 安装mocha包,我并没有选择全局安装.只是在mocha的文件夹里的Package.json添加mocha的依赖. 如下图 { "name": "mocha-test", "version": "0.0.1", "dependencies": { "koa": &quo