上一章了解了基本的术语,我们知道了在分块(函数)中,代码顺序是不会被打乱的,但是在分块之上,就无法确定谁先谁后了。

无论一个 JS 代码写的多复杂,回调函数是JS异步编程的基础,如果 JS 的异步没有缺点该多好,多少开发者将为这个承诺(双关 Promise)欢呼,然而就算你知道 promise 是对回调函数的抽象,你要先知道为什么要抽象,才能真正理解后面的东西。

本章的主要内容就是告诉你,为什么我们需要更好的异步模式。

延续 (Continuations)

回调(callback)函数就是对未来要执行的事件进行封装。关键是要理解回调函数表达与管理时出现的各种难以应对的情况

线性的大脑

你可以一边说话一边打字,但你只不过是在两种任务间来回切换而已,这和JS的执差不多。然而问题出在了代码书写上,你不能用线性的大脑去线性的读 JS 异步代码,这是回调函数的最大缺点。

嵌套回调

你想让回调代码让人看的清晰,就会出现 回调地狱 ,也就是常说的 恶魔金字塔 。有时候你还要判断一个函数做为参数也可能不是回调。实际工作中JS代码的混乱程度有过之而无不及。这还不是最要命的,要命的是我们之前说过的那些竞争条件。

信任问题

你开发了的一个程序,里面有个回调函数,会获取定单金额,过去半年,突然要改,要你加入一个门闩,因为可能要从多个位置获取数据,然后过几天QA又要你写一个回调没有返回的错误处理等等,你不得不加入许多后验机制防止各种毛病。这样你要为每一个回调函数里添加许多代码。

别人的代码

你可以写代码,别人也写, 你们互相使用,但是如何使用才能不出意外,也许你要让一个函数接受 数字作参数,别人就传入字符串会出错,这时你你需要在编写时就遵循“信任但验证”的原则。回调函数也应该这样。

拯救回调

为了解决后验问题,出现了两种回调设计模式 ,一种是分离设计(Promise),另一种是错误优先处理(node.js)。

但是这并没有解决根本问题?比如你要取消回调,一个回调函数是不是太早被调用了,还有就是分不清到底一个函数作参数是不是回调(Zalgo怪兽,因此要遵循传入的函数都是回调的设计原则)等等问题。

传入的到底是不是异步函数呢?

var a = 0;

show(sync);  //=> 0
show(Async); // 1秒后 => 1

a++;

function show(arg) {
    arg();
}

function sync() {
    console.log(a);
}

function Async() {
    setTimeout(function(){
        console.log(a)
    },1000)
}

小结

  1. 回调函数是 JS 异步编程的基本组成单元。
  2. 人的大脑是单线程工作的,但 JS 代码并没有表现出线性关系,人很难一眼看出运行顺序,如果写的代码看不懂,那就容易出bug
  3. 我们需要一种符合人脑思维的代码书写形式来表达异步
  4. 使用各类库实际上在来回交换回调的控制权,这就会导致许多信任问题
  5. 我们不可能因为回调的缺点就不使用它,因为那是不可能的,必须出台一揽子计划解决方案,未来的 JS 规范正尝试出台这样的政策,即我们接下来要讲的。