理解Javascript(NodeJS)中的回调函数
1、回调函数的概念
A callback is a function that is passed as an argument to another function and is executed after its parent function has completed
回调函数就是一个参数,将这个函数作为参数传到另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数。这个过程就叫做回调。
示例:
1//定义主函数,参数是一个回调函数
2function A(callback callback();
3 console.log('我是主函数1<br/>');
4 console.log('我是主函数2<br/>');
5 console.log('我是主函数3<br/>');
6}
说明:
回调函数本身就是一个数据类型。
在javaScript中,函数的地位和java中的String,int,boolean等等都是一样的,都可以看成是一个数据类型。
既然是数据类型,就可以当成参数传递(如:callback)
函数体中打了个括号(** callback(); **)就是执行函数的意思
把函数传入A 方法的目的就是让函数在里面执行
1//定义回调函数
2function B(){
3 setTimeout("console.log('我是回调函数')", 3000);//延时3秒,模仿耗时操作
4}
5
6//调用主函数,将函数B传进去
7A(B);
输出:
我是主函数1 我是主函数1 我是主函数1 我是回调函数
定义主函数的时候,我们让代码先去执行callback()回调函数,但输出结果却是后输出回调函数的内容。
这就说明了主函数不用等待回调函数执行完,可以接着执行自己的代码 ( 异步,非阻塞 )。所以一般回调函数都用在耗时操作上面,比如ajax请求,文件处理等。
2、异步的概念
假定有一个需求: 全局变量a初值为0,然后过3秒后,让它为6,然后再打印出来,首先想到的写法如下:
1var a = 0;
2
3function B(x) {
4 console.log(x);
5}
6
7function A(time) {
8 setTimeout(function () {
9 a=6;
10 }, time);
11}
12
13//调用:
14console.log(a);
15A(3000);
16B(a);
看上去,上面的代码没有问题,理论上符合我们的逻辑需求,但却发现结果是这样:
0 0
咋回事?
因为JS是一种异步执行语言,尽管timer函数内让a=6了,但是JS不会死等时间结束再跳出函数,
而是马上就会执行下一步语句(即调用B函数),但这时候3秒钟根本就没结束,a还没有被重新赋值,
所以打印出来还是为0。
用回调函数可以解决这个问题:
1var a = 0;
2
3function B(x) {
4 console.log(x);
5}
6
7function A(time, callback) {
8 setTimeout(function () {
9 a = 6;
10 callback(a);
11 }, time);
12}
13
14//调用:
15console.log(a);
16A(3000, B);
这次,在A函数中添加了一个关键字callback,这不是一个普通的参数,而是一个函数参数。
一般而言,函数的形参是指由外往内向函数体传递变量的入口,
但此处加了callback后则完全相反,
它是指函数体在完成某种使命后调用外部函数的出口!
这时候应该明白什么叫“回调”了吧,也就是回头调用外部函数的意思。
在本例中,当3秒钟到了后,首先a=6,然后通过关键字callback(a)调用了函数B(x),结果显示:
0 6
这个逻辑,符合我们的需求。
在写法上,也可以不需要定义函数B, 直接在调用A的时候写成function形式,
把调用部分改成这样也可以,效果完全一样:
1console.log(a);
2A(3000, function (x) {
3 console.log(x);
4});
这种写法函数名都不需要了(术语称为“匿名函数”),在nodejs代码中更为常见也更好理解,翻译成自然语言就是:定时3秒,完成后再回头调用function(x)里面的内容。
nodejs编程中大量使用了异步编程技术,这是为了高效使用硬件,同时也可以不造成同步阻塞。
其实nodejs在底层还是通过多线程技术实现的异步操作,但普通用户并不需要深究它的实现方法,我们只要做好异步处理即可。
因为可以把调用者与被调用者分开,所以调用者不关心谁是被调用者。它只需知道存在一个具有特定原型和限制条件的被调用函数。
回调函数就是允许用户把需要调用的函数的指针作为参数传递给一个函数,以便该函数在处理相似事件的时候可以灵活的使用不同的方法。
参考:https://blog.csdn.net/rockage/article/details/79513450
================================================================
实例理解回调函数
例如有两件事情需要完成:
1、扫地
2、烧开水
按 单进程、同步 、阻塞的方式进行:
1、第一步:先打扫卫生
2、第二步:卫生打扫完毕,接着烧开水
按 单进程、异步 、非阻塞 的方式进行则按顺序进行:
1、第一步:开始打扫卫生
2、第二步:到烧开水的时间,使用有 鸣叫功能(回调函数) 的开水壶烧开水
3、第三步:不用守着烧开水,接着打扫卫生 (非阻塞)
4、第四步:听到开水烧开的声音 (调用回调函数),去处理开水
5、第五步:开水处理完毕,接着扫地