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

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

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

基于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 name='"+myphone+"'",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()读取文件。

总结:

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

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

  1. Node.js学习笔记(2):基本模块

    Node.js学习笔记(2):基本模块 模块 引入模块 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式.在No ...

  2. 一点感悟:《Node.js学习笔记》star数突破1000+

    写作背景 笔者前年开始撰写的<Node.js学习笔记> github star 数突破了1000,算是个里程碑吧. 从第一次提交(2016.11.03)到现在,1年半过去了.突然有些感慨, ...

  3. Node.js学习笔记(3):NPM简明教程

    Node.js学习笔记(3):NPM简明教程 NPM常用操作 更新NPM版本 npm install npm -g -g,表示全局安装.我们可以指定更新版本,只需要在后面填上@版本号即可,也可以输入@ ...

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

    Node.js学习笔记系列总索引 Nodejs学习笔记(一)--- 简介及安装Node.js开发环境 Nodejs学习笔记(二)--- 事件模块 Nodejs学习笔记(三)--- 模块 Nodejs学 ...

  5. Node.js学习笔记(1):Node.js快速开始

    Node.js学习笔记(1):Node.js快速开始 Node.js的安装 下载 官方网址:https://nodejs.org/en/ 说明: 在Windows上安装时务必选择全部组件,包括勾选Ad ...

  6. Node.js学习笔记(4):Yarn简明教程

    Node.js学习笔记(4):Yarn简明教程. 引入Yarn NPM是常用的包管理工具,现在我们引入是新一代的包管理工具Yarn.其具有快速.安全.可靠的特点. 安装方式 使用npm工具安装yarn ...

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

    很多node入门的书里面都会在介绍node特性的时候说:单线程,异步式I/O,事件驱动. Node不是一门语言,它是运行在服务器端的开发平台,官方指定语言为javascript. 阻塞和线程: 线程在 ...

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

    Node.js 异步编程的直接体现就是回调. 那什么是回调呢?回调指的是将一个函数作为参数传递给另一个函数,并且通常在第一个函数完成后被调用.需要指明的是,回调函数不是由该函数的实现方直接调用,而是在 ...

  9. Node.js学习笔记(一)

    1.回调函数 node是一个异步事件驱动的平台,所以在代码中我们经常需要使用回调函数. 例: setTimeout(function(){ console.log('callback is calle ...

随机推荐

  1. JavaScript (JS)基础:ECMAScript 浅析 (含Math基本方法解析)

    浏览器端JavaScript的组成 ECMAScript:语法规范 DOM:Document Object Model  文档对象模型,操作页面元素 BOM:Browser Object Model  ...

  2. BZOJ 1497: [NOI2006]最大获利

    1497: [NOI2006]最大获利 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 4572  Solved: 2239[Submit][Status] ...

  3. set与map的区别

    STL中:MAP的节点是一对数据.   SET的节点是一个数据. Map使用关键值Key来唯一标识每一个成员 map可以重复.set是集合 都属于关联容器   只不过,   map的形式   map& ...

  4. 存储过程代码生成器Stored Procedure Generator

    原文发布时间为:2010-10-26 -- 来源于本人的百度文章 [由搬家工具导入] Stored Procedure Generator (for SQL Server 2000/2005) htt ...

  5. Eval的格式,主要看大括号的格式,其他不用那样写

    原文发布时间为:2009-08-26 -- 来源于本人的百度文章 [由搬家工具导入] 使用范例:<%#Eval("date","{0:d}") %> ...

  6. sql联合主键,用于多对多,关系映射

    如题.记录下. 复合主键,由多个字段共同确定一行信息 composite key, containing multi cols to fix one element.

  7. check source code after macro expand

    Some time I'd like check source code after macro expand. We can use -E option to stop after the prep ...

  8. 网络编程socket-SocketServer-FTP

    16章 网络编程?应该是讲网络之间如何编程来进行通信的章节 16.1.1 客户端/服务器架构?客户端请求访问,服务器端监听请求,处理请求,进行相应的模式16.1.2 客户端/服务器编程?服务器端:创建 ...

  9. LeetCode OJ--Combination Sum **

    https://oj.leetcode.com/problems/combination-sum/ 给一列数,3 2 1 3 3 8 7 9 ,每个数可以重复多次,给target 7, 问可以加起来得 ...

  10. java gc --- 关键词解释

    分代gc java的堆内存主要分为young generation与old generation,这两块分开回收.这就是所谓的分代gc 其中young generation又分为一个eden spac ...