你不知道的JS(十三):回调

  • 在前面所有的示例中,函数都是作为回调使用的,因为它是事件循环“回头调用”的目标。
  • 回调函数是JavaScript的异步主力军,但是回调函数也不是没有缺点的。

一、程序的延续

  • 回调函数包裹或者说封装了程序的延续。
  • 看一段简化的代码,描述执行顺序:
// A
setTimeout(function() {
	// C
},1000);
// B
  • 有些人会描述,先执行A,设置延时1000毫秒,然后执行B,1000毫秒到时后执行C。
  • 这样解释是不足的(不够精确),这也是回调作为异步表达和管理方式的缺陷的关键所在。
  • 如果存在多个回调函数,那么就会难以理解、追踪、调试和维护。

二、顺序的大脑

  • 我们大脑的工作方式优点类似于事件循环队列。
  • 大脑在特定的时候,只能思考一件事情。我们好像并行执行多个任务,但实际上可能是快速的上下文切换。
  • 也就是在多个任务之间快速地来回切换。
  • 我们切换得非常快,对于外界来说,我们就像在并行地执行任务。

1. 执行与计划

  • 我们大脑先安排顺序,然后按照顺序(A,然后B,然后C)执行,如果有某种形式的拥塞,会保证B等待A完成,C等待B完成。
  • 开发者编写代码的时候是在计划一系列动作的发生
  • 问题是,代码(通过回调)表达异步的方式并不能很好地映射到同步的大脑计划行为。
  • 我们思考方式是一步一步的,但是从同步切换到异步后,回调却不是按照一步一步的方式来表达。
  • 这就是为什么精确编写和追踪使用回调的异步JavaScript困难的原因。

2. 嵌套回到和链式回调

  • 多个函数嵌套在一起,每个函数都代表异步序列(多个有顺序的异步任务)中的一个步骤,这种代码常常被称为回调地狱

顺序匹配

  • 实际上,回调地狱与嵌套和缩进几乎没有什么关系。
  • 这只是其中的一个问题,它引起的问题要比这个严重得多。
// 伪代码,function()代表回调函数
doA(function() {
	doB();
	doC(function() {
		doD();
	})
	doE();
});
doF();
  • 尽管有经验的你能够正确描述出实际的运行顺序,但这需要费一番脑筋才能想清楚。
  • 实际顺序:doA() => doF() => doB() => doC() => doE() => doD()
  • 我们不得不上下来回查看哪个函数先被调用。想象一下如果异步任务更多,那么将更加复杂。
  • 但是,这是假定异步的情况下。如果doA()doD()实际并不是异步,或者在某些情况下并不是异步的,那么这个顺序就更加困难了。

脆弱性

  • 这样硬编码还会使代码更脆弱,因为它没有考虑可能导致步骤执行过程中的异常情况。

  • 一旦你指定了所有可能事件和路径,代码就会变得非常复杂。

  • 比如,如果步骤2失败,就永远不会到达步骤3,不管是重试还是跳转到其他错误流程。这个问题也可以解决,但是代码通常是重复的。(成功和失败的程序中的代码重复了)

  • 这才是回调地狱的真正问题所在

三、信任问题

  • 回调最大的问题是控制反转,它会导致信任链完全断裂

  • 我们把自己程序一部分的执行控制交给某个第三方,称为控制反转

  • 我们使用第三方库的API,将回调函数传入的时候,可能会导致一些问题。

  • 比如:在这个第三方库里面,因为某些错误将这个回调执行了多次。你可能只要它执行一次,但这不是你可以控制的。

    当然,不只是次数问题,还有

    • 调用回调过早
    • 调用回调过晚(或没有调用)
    • 调用次数过多或过少
    • 没有成功地将参数传入到回调中
    • 吞掉可能出现的报错和异常
  • 这不只是针对外部代码,对于我们自己控制下的代码,也可能会有问题。

四、尝试挽救回调

回调设计存在几个变体,意在解决前面讨论的一些信任问题。

1. 处理错误

  • 分离回调设计:一个用于成功通知,一个用于出错通知。比如ES6的Promise API。
  • error-first风格:回调中第一个参数预留作为错误对象,如果成功,这个参数会置假。

2. 调用过早

  • 对于既可能在现在(同步)也可能在将来(异步)调用你的回调的工具来说,会有明显的问题。
  • 这种由同步或异步行为引起的不确定性总会带来极大的bug追踪难度。
  • 永远异步调用回调,这样所有回调都是可以预测的异步调用了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值