回调函数的处理逻辑理解:
所谓的回调函数处理逻辑,其实就是先将回调函数的代码 冻结(或者理解为闲置),接着将这个回调函数的代码放到回调函数管理器的队列里面。
待回调函数被触发调用的时候,对应的回调函数的代码才会从管理器队列取出来并自动执行(激活回调函数的代码),实现异步的编程效果。
关于回调函数的另外一个更加形象的说法:
回调函数一个实例:儿子去做一个事情,完成之后父亲会给一个红包(给儿子)。然后父亲立刻去忙别的事情了。
分析例子:
首先,事情必须要完成之后才可以给红包。另外,红包的钱是父亲给,儿子只需要在完成之后,自己拆开红包即可。当然,儿子并不用知道父亲会给多少钱。
很简单吧?其实回调函数也就是这样子的思路。
儿子去做一个事情–>调用儿子的做事情的函数方法。儿子的函数方法是异步的,并不会影响下一步动作:父亲工作。
红包–>回调函数,也就是callback(通常是匿名函数)。儿子只需要确定拆开它(执行它)即可。至于里面是什么,有多少钱,儿子管不了。
红包里面的钱–>可以理解为回调函数所执行的代码段。由调用者提供!决定给多少钱。
儿子完成之后才可以拆红包–>儿子的工作函数完成之后,才会调用所提供的回调函数(也就是拆红包,数钱)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
//父亲对象定义: var father={ pay:function(money){ account.pay(money);//父亲账户付款 }, 工作:function(){xxxxxx} } //儿子对象定义: var son={ work:function(callback){//儿子的工作函数 //辛辛苦苦的干活 working(); //活干完之后,执行回调函数(拆红包) callback; }, receive:function(money){ account.rec(money);//儿子账户收款 } } //现实生活对应程序: //儿子去做一个事情。完成之后父亲会给一个红包(给儿子)。然后父亲立刻去工作了。 son.work(function(){ var money=200; father.pay(money);//父亲付款200元 son.receive(money);//儿子收款200元 }); //父亲继续做别的事情 father.工作(); |
现在问题来了,对于嵌套的回调函数应该如何理解呢?
对于一些必须要等回调函数执行完毕才可以确定的动作,实际上,就是会掉入一个层层回调的局面了。这点也是回调函数的复杂的地方!也就是所谓的嵌套回调函数。
换句话说,只要您要获取的值是要被回调才确定的,那接下来的所有代码都必须用回调的方式来编写,层层嵌套。
以百度提问的一个例子简单说明:
问题:
js 回调函数问题的执行结果想作为返回值
1 2 3 4 5 6 7 8 9 10 |
function receive() { var re_data; client.on('data', function(data) { re_data = data; console.log(data); client.end(); }); return re_data ; } |
receive这个函数的目的是返回接受到的数据(data),但data值在一个回调函数里面,总是先执行return ,再执行回调函数,因此返回值总是undefined,有什么办法可以解决吗?
解决办法:
建议改变你的思维方式,将你的函数 receive 也改成回调式的:
1 2 3 4 5 6 |
function receive(callback){ // .... client.on('data', function(data) { callback(data); }); } |
然后,别的地方要调用receive函数,则将这个函数获取的值之后的所有动作都封装在回调函数callback里面。
例如:
1 2 3 4 5 |
aaaa; receive(function(data){ //这里下面的代码都可以获取data了。 data....的处理。。。 }); |
上面的只是一个很简单的例子,
如果有3层以上的嵌套回调函数,理解方面是不容易的。下面,我就以一个现实生活中的买玻璃的例子来讲述嵌套回调函数的执行过程!
举个容易理解的实际例子:
我向要二级供应商要2块镀膜的建筑玻璃。我不需要知道这个玻璃怎么制作的,玻璃怎么到一级供应商的,怎么到二级供应商的。
反正你二级供应商能提供给我要求的玻璃就可以了,提供之后要告诉我是多少价格的,好让我准备付款给你。
–>需要注意的是,我收到了建筑玻璃,我才可以做安装玻璃等等的动作。但是,拉屎拉尿什么的,我想什么时候做就什么时候做,和购买玻璃并没什么关系。
上面的例子就是一个典型的嵌套回调函数实例!
首先,开始模拟并构造基本函数:
–>制造过程函数:
1 2 3 4 5 6 7 8 9 10 |
var make = function(qty,type,callback){ //接到订单,也要检查数量和类型。有些类型可能无法生产! makeCheck(); //根据qty数量和type类型来制造需要的玻璃 //要经过很多道工序的生产。。。 factory.makeGlass(); //做完之后,就调用回调函数,通知已经制造好啦!可以来拿货。当然要收钱啊。就是提供价格给调用者(批量出厂的价格)。 var amount=100元 callback(amount); } |
–>一级供应商函数:
1 2 3 4 5 6 7 8 9 10 11 |
var vendor1 = function(qty,type,callback){ //接到订单,也要重复做检查数量和类型。 vendor1Check(); //向工厂订货。要的数量和类型是需要指定的。一级供应商不需要知道怎么制造的,只需要通知工厂要货即可 make(qty,type,function(amount){ //工厂制造完毕,接到工厂的报价之后,这里一级供应商当然要赚钱,所以要加价10% var vendorAmount=amount*1.1; //执行父亲提供的回调函数代码,并且通知一级供应商提供的价格是多少(主动提供回调函数的参数)。 callback(vendorAmount); }); } |
–>二级供应商函数:
1 2 3 4 5 6 7 8 9 10 11 12 |
var vendor2 = function(qty,type,callback){ //接到订单,接着检查数量和类型是否正确。是否可以生产这种类型的玻璃。 vendor2Check(); //二级供应商不需要知道货是哪里拿的,只需要通知一级供应商要货即可 //接着向一级供应商订货。要的数量和类型是需要指定的 vendor1(qty,type,function(amount){ //货到,接到一级供应商的报价之后,这里二级供应商当然要赚钱,所以要加价20% var vendorAmount=amount*1.2; //执行对外的回调函数,并且通知用户,它提供的价格是多少。 callback(vendorAmount); }); } |
–>用户函数:
用户是最终的消费者。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
var user = { //用户购买玻璃的函数方法: userBuy: function(qty,type,callback){ //核对数量是否正确,玻璃类型是否是需要的。 userCheck(); //用户向二级供应商拿货。需要注意的是,到货之后就可以进行安装等动作。都写在callback里面。 vendor2(qty,type,function(amount){//调用二级供应商的接口。并且写好回调函数,知道价格之后做什么。 //这里写的是玻璃到货之后的执行的动作。可能是10天之后才执行的。 user.receive();//用户收货 user.check();//用户检查货物质量 user.pay(amount);//用户付款 callback;//这里执行一个回调函数,可以是安装玻璃,甚至可以送给别人等等。由调用者决定做什么事情。 }); }, 吃饭:function(){xxxxx}, 上厕所:function(){xxxxx}, sleep:function(){xxxxx} } |
基于上面所定义的函数方法,模拟一个现实的例子:
用户早上吃饭,中午买玻璃,下买玻璃的订单之后,接着拉个屎,再睡觉。
试想一下,如果要等到玻璃来到之后才可以上厕所,那岂不是被憋死了。
—-程序处理过程:
1 2 3 4 5 6 7 |
user.吃饭(); //买玻璃 user.userBuy(10,'镀膜建筑玻璃',function(amount){ user.安装玻璃(); }); user.上厕所();//由于买玻璃是异步函数,所以,这里发出买玻璃的请求(调用买玻璃的函数)之后,立刻可以去上厕所了,片刻不影响! user.sleep();//用户上厕所完毕,舒服了,睡觉去! |
上面的例子可以看到,实际上回调函数是层层执行的。执行完最深的那层,再逐一向上执行。
执行用户购买玻璃的方法的时候,程序的执行顺序:
主函数的执行顺序:
userBuy(用户调用购买货物的方法) 父
–>vendor2(二级供应商提供货物的方法) 子
–>vendor1(一级供应商提供货物的方法) 孙
–>make(工厂制造过程的方法) 曾孙
而每个主函数所编写的的回调函数的代码的执行顺序:
make.cb(工厂制造完毕,会调用回调函数[执行(父级的)一级供应商提供的代码],相当于通知一级供应商,货物已经完成!价格是多少。)
–>vendor1.cb(一级供应商确认工厂制造完成,调用回调函数[执行二级供应商提供的代码],通知二级供应商,货物已经有了,价格是多少。这时候的价格已经加价10%)
需要注意的是,必须要等到生产完成并知道工厂提供的实际价格是多少,才可以加价。所以加价的代码由vendor1主动提供,但是执行由工厂用callback执行!
–>vendor2.cb(二级供应商拿到货,调用回调函数[执行用户购买所提供的代码],通知用户,货物已经有了,价格是多少。这时候价格又加了20%。真黑!)
–>userBuy.cb(用户拿到货了,先验货并付款,并调用回调函数[执行玻璃到货之后的代码,也就是调用user.userBuy的时候编写的回调代码,例如安装玻璃什么的]。流程走完!)
可以看出,嵌套的回调函数的执行顺序是逆向逐步执行。
每一个回调函数代码只和其儿子有关系。儿子做完之后,自动执行回调函数[父亲所写好的代码]。
所以,现在让我们理解一下所谓的嵌套的异步回调函数:
主函数的代码实际上还是按照顺序执行的。
只要是封装在回调函数里面的代码,都是要等待最深层的回调函数执行之后,才会逐一执行前一层的回调函数的代码!
另外一个理解:我不需要知道你是怎么做的,你只需要做好之后告诉我,并且告诉我结果,即可。我只要结果!
如果是要等到知道结果才可以继续做的事情,则就是要写在等待别人告诉我之后才可以做的事情列表里面(回调函数的事情);
如果是别的事情,例如上班什么的,立刻就去做,无需等待!
总结:
所谓的回调函数,就是由调用函数提供执行代码,被调用函数执行完毕之后,再自动执行的一个函数。异步式。
如果是嵌套的回调函数,就是,调用函数的回调函数嵌入到被调用函数所运行的回调函数代码之间(执行)的函数。就是callback代码中的callback函数。嵌套回调!