回调函数包裹或者说封装了js程序的延续
回调地狱
多个函数嵌套在一起构成的链,每个函数都代表异步任务中的一个步骤,这种代码被称为回调地狱,举例!
listen("click",function handler(evt){
setTimeout(function request(){
ajax("....",function response(text){
if(text == "hello"){
handler();
}else if(text == "world"){
request();
}
})
},500)
})
这种满是回调的代码,想要理解其中的异步流是非常难的,我们不得不频繁的从一个函数跳转到另一个,再从另一个继续跳转,在整个代码中跳来跳去以来查看其流程,虽然也可以通过手工硬编码来解决(有关联的步骤直接编写在一起),但这样的代码通常是重复的且在其他步骤中无法复用,代码会写的很“死”且复杂,对于后期维护和更新来说就是噩梦
信任问题
先看一个例子
// A
ajax("...",function(){
//C
})
// B
A和B发生与现在,在javascript主程序的直接控制下,C会延迟到将来发生,并且是在第三方的控制下——本例中为ajax函数,而有的时候,第三方不是你编写的代码,也不在你的控制下,它是某个第三方提供的工具
这称为控制反转,也就是把自己程序的一部分执行控制交给某个第三方,这是一个非常容易忽略但回调驱动设计最严重的问题,因为不是自己写的,所以有不可控性。
举个例子,假如回调只需要调用一次,但第三方的那个工具出现了意料之外的问题导致调用多次,将会引出各种奇怪的bug,这种情况虽然也可以通过建立一个latch来处理对回调的多个并发调用,但问题不止于此,还可能出现各种情况如:调用过早/过晚,调用次数过少,没有把所需环境/参数传给回调函数等,你不得不对无法信任的工具的每个回调,都创建大量的混乱逻辑,最后诞生宛如地狱一样的代码
虽然也可以通过发明一些特定逻辑来解决这些信任问题,但其难度高于应有的水平,可能产生更笨重,难维护的代码,其中的损害要直到出现bug才能被发现