Node.js 异步编程的直接体现就是回调。
那什么是回调呢?回调指的是将一个函数作为参数传递给另一个函数,并且通常在第一个函数完成后被调用。需要指明的是,回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。回调函数在完成任务后就会被调用,Node 使用了大量的回调函数,Node 所有 API 都支持回调函数。例如,我们可以一边读取文件,一边执行其他命令,在文件读取完成后,我们将文件内容作为回调函数的参数返回。这样在执行代码时就没有阻塞或等待文件 I/O 操作。这就大大提高了 Node.js 的性能,可以处理大量的并发请求。
虽然异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了。
在开始下面的内容之前,我们需要先了解几个概念:
什么是阻塞:
所谓阻塞,即每次执行一个操作,在一个操作完成之前,代码的执行会被暂停,无法继续下一个操作。
什么是非阻塞:
所谓非阻塞,指的是基于回调、允许脚本并行执行操作的方法,操作结果会在事件发生时由回调来处理,从而无需等待某个操作的结果继续下一步。
举个栗子解释一下阻塞和非阻塞的区别:
你在家里做饭,结果发现家里没酱油了,所以你就停下手上的活跑去商店买酱油。结果店员告诉你说现在没酱油了,采购员正在外面采购,需要等一下才回来。这时候你就有两个选择:一是继续在商店里等采购员回来,拿到酱油回家继续做菜;二是先回家忙其他的活,等一下再回来买酱油。
第一个选择其实就是阻塞的做法,需要等到酱油买到之后才继续其他的事。第二个选择就是非阻塞的做法,先去做其他的事,等到合适的时候再去买酱油。(当然,这栗子有点不靠谱。希望大家能看懂我的意思。)
实践出真知,讲完基础的知识点,那就开始敲一下代码吧。撸起袖子就是干。
现在这里过一下下面实例会使用到的API:
读取文件(同步操作):fs.readFileSync(file[, options])
读取文件(异步操作):fs.readFile(file[, options], callback)
阻塞代码实例
先新建一个txt文件,比如demo2.txt。然后在里面随便敲一点东西,比如:
****** this is my second demo! ******
然后再在同个目录下新建一个js脚本,我把它取名为demo2.js,代码如下:
//引入fs(filesystem)模块,在脚本使用 var fs = require("fs"); //同步读取文件 var data = fs.readFileSync(‘demo2.txt‘); //打印数据 console.log(data.toString()); console.log("------ 程序执行结束! ------");
最后就可以使用node指令运行我们的代码了!是不是很心急,那就来看看吧。
//node指令 node demo2.js
cmd运行效果:
可以看到,js脚本读取完文件并输出文件内容之后,才会继续执行下面的console.log("------ 程序执行结束! ------")。由于读取文件和输出内容需要时间,在读取和输出的过程中脚本并不会先去执行其他代码,而是要等到读取并输出文件内容完成之后才会接着去进行下一步操作。这就是所谓的(同步)阻塞。
非阻塞代码实例
demo2.txt仍跟之前的一样。我们只要修改demo.js代码就行,具体代码如下:
//引入fs(filesystem)模块,在脚本使用var fs = require("fs"); //异步读取文件 fs.readFile(‘demo2.txt‘, function (err, data) { if (err) return console.error(err); //若读取失败,则报出错误 console.log(data.toString()); //读取成功则输出文件内容 }); console.log("程序执行结束!");
来看看运行效果:
可以看到,由于使用了异步读取操作,所以在读取文件的时候,脚本会继续执行下面的代码,也就是说,不管文件是否读取并输出完毕,脚本都会往下执行。所以就会看到先执行完console.log("------ 程序执行结束! ------")之后才看到输出"****** this is my second demo! ******"。这就是所谓的(异步)非阻塞。
以上两个实例我们了解了阻塞与非阻塞调用的不同。第一个实例在文件读取完后才执行完程序。 第二个实例我们不需要等待文件读取完,这样就可以在读取文件时同时执行接下来的代码,大大提高了程序的性能。
因此,阻塞是按顺序执行的,而非阻塞是不需要按顺序的,所以如果需要处理回调函数的参数,我们就需要写在回调函数内。
你们看懂了吗?反正我是懂了(~ ̄▽ ̄)~