[JS] You Don't Know JavaScript [Async & Performance] - callbacks

前言

在JavaScript中Callback是最基础的也使最常用来处理非同步的方式,而callback是什麽?简单来说就是在另一个函数完成执行之後执行就称为callback,而复杂来说由於JavaScript中的函数属於一级成员,代表着函数可以作为参数传递并且可以由其他函数返回,而作为参数传递的任何函数都称为callback function,使用callback function虽然可以解决非同步的问题,但是他同时也有许多缺点,本章终将仔细地介绍callback function的缺点。


Why do we need Callback Functions?

在介绍callback的缺点之前,让我们再复习一次倒底JavaScript为什麽会需要一个callback function,由於JavaScript是个由上而下运行的程序但是某些情况下会需要使用非同步的方式运行,而callback function可以确保他在我们指定的任务完成之前不会被运行,但当我们指定的任务完成时他会立刻运行,这使我们在开发非同步程序的时候能够确保我们的流程顺序不会有错误。

让我们举个例子,当我们开发了一个专案,我们需要使用ajax获得後端的数据再将它显示出来,我们需要保证我们确实拿到了後端的资料(ajax已经完成)後才将它显示出来不然会发生错误,这样的情况下使用callback function就能够确保我们的时间顺序,不会在ajax任务尚未完成之前就将data显示。

ajax("https:ajax.com", function(data){
    console.log(data);
})

What is callback funciton Disadvantages?

我们了解了callback function最初被设计出来是为了要解决什麽问题,他确实能够在我们处理非同步事件的时候起到一的很好的作用,但是他确实也有需多缺点,让我们一一介绍他有哪一些缺点:

Order problem

当我们在阅读别人的程序的时候,由於我们习惯由上而下的阅读所以对於非同步的程序来说我们会看得稍微有点吃力,让我们举个例子:

// a...
setTimeout(function(){
    // c...
},1000);
// b

当我们要将上面这个程序解释给一个JS新手时,我们该如何解释他?

  • 可能会有人说:先做a,设置一个等待一秒的定时器,一秒过後执行c。
  • 也可能会说:先做a,设置一个等待一秒的定时再去做b,一秒过後再回来做c。

虽然第二种说法比较正确,但是说实在的如果我是一个JS新手估计我会完全不知道你在说什麽,为什麽程序执行到下面後又跳回上面?这样的问题出现在许多开发人员心中。

你可能觉得:这样就不行了也太弱了吧...那是因为我们只介绍了简单的callback问题,让我们再来一个更难的:

doA(function(){
    doB();
    
    doC(function(){
        doD();
    })
    
    doE();
});
doF();

看到上面的程序估计已经晕的,如果是一个新手来看这个程序我想他已经在放弃的路上了,这个程序的真实发生顺序:doA -> doF() -> doB() -> doC() -> doE() -> doD()

当我们在追踪这种非同步事件的程序时,常常会因为使用callback function导致我们我需要再一个程序中不断上上下下的去看执行的顺序,这对於日後维护或是其他人来看都会是一个不便的事情。
https://ithelp.ithome.com.tw/upload/images/20210118/201247677dkFwCQn1U.png

Callback Hell

使用callback function除了会让阅读程序因为顺序问题不断上上下下增加阅读难度之外,还有一个在JavaScript中流传已久的callback hell,让我们来举个例子实际体验一下地狱:

listen('click', function handler(event){
    setTimeout(function request(){
        ajax('https://some.url.1'm function responde(test){
            if(test === 'hello'){
                handler();
            } else {
                request();
            }
        })
    })
})

由於JS中函数属於一级公民可以作为参数传递,所以就会有这种多重嵌套的问题,我们来分析一下上面程序:

  1. 首先我们先注册一个监听事件
listen('click', function handlet(event){
    // ...
})
  1. 当发生监听事件时触发计时器在500ms之後触发callback function
setTimeout(function request(){
    // ...
})
  1. 计时结束後触发callback function ajax进行数据请求
ajax('https://some.url.1'm function responde(test){
    // ...
})
  1. 当获得ajax数据後再对数据进行判断
if(test === 'hello'){
    handler();
} else {
    request();
}

当看完这些程序逻辑後估计已经头晕了,不过这只是简单的三个嵌套,还有更恐怖的...
https://ithelp.ithome.com.tw/upload/images/20210118/20124767CQmRdUH1mA.png


结论

callback function是JS中处理非同步的基础,但是随着JS的成熟与更加庞大的需求它们对於非同步编程的演化趋势来讲显得不够。

我们对於阅读程序会习惯由上到下的同步化、顺序化阅读而如果使用callback function会因为执行顺序的问题导致程序上上下下的执行,对後续的维护与阅读造成很大的困扰,并且若嵌套多层callback function则这个问题更加明显。


参考文献

You Don't Know JavaScript
JavaScript: What the heck is a Callback?
JavaScript Callback Functions – What are Callbacks in JS and How to Use Them


<<:  # iOS APP 开发 OC 第十八天,MRC 实作

>>:  iOS APP 开发 OC 第十八天,Wild Pointer 如何火化殭屍?如何将殭屍复活?

C# SqlCommand和SqlDataAdapter的区别

SqlCommand对应DateReader SqlDataAdapter对应DataSet Sq...

wordpress更新出现Briefly unavailable for scheduled maintenance. Check back in a minute.

今天在更新wordpress插件时出现了Briefly unavailable for schedu...

[Day0] Vite 出小蜜蜂~和卡比一起玩网页游戏开发!

Day0 动机 Motivation 80 年代对卡比来说是个很特别的年代, 那个年代的音乐、影视、...

D21 第十一周 (回忆篇)

这礼拜还是再追第九周的进度,因为额外研究了其他东西的关系。 apache alias js 剪贴簿 ...

Day26练习java-抛出例外

昨天使用的是在有明确资讯时的例外处理,可以直接使用try catch抓出来,但有时在方法里需要传入资...