回调地狱

本篇将介绍一下什么是回调地狱

回调地狱

异步编程 之一 回调地狱

对于回调地狱(Callback hell),想必大家都不陌生,但是在RxJava、Reactor等反应式编程框架兴起之后,对于回调地狱只是听得多,但是见得的少。所以接下来将介绍一下什么是回调地狱

回调函数

要了解什么是回调地狱,那就得先了解一下什么是回调。简单来说回调就是:调用者在调用被调用者后,被调用者进行操作并将结果反馈给调用者。这个过程就是回调

代码讲解

普通回调

  1. 回调嘛,得先创建个Callback接口,用来回调

    1
    2
    3
    4
    5
    6
    7
    interface Callback<T> {

    /**
    * 定义一个回调的接口,方便下面的实现
    */
    fun callback(value: T)
    }
  2. 接下来我们就已工作中上司和下属来作为例子。创建上司

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 上司肯定要有下属嘛,所以得就将下属传进来
    class Boss(private val subordinate: Subordinate) : Callback<String> {

    override fun callback(value: String) {
    println("上司收到通知:工作${value}已完成")
    }

    /**
    * Boss安排任务
    */
    fun arrangeWorker(worker: String) {
    println("上司开始安排任务")
    Thread{ // 开启了个新线程来通知下属去工作
    subordinate.work(worker, this)
    }.start()
    println("老板下班^^")
    }
    }

    老板分配工作,员工去完成,如果完成过程是异步,则是异步调用如果是同步的,则是同步回调,我们这里采用异步方式。

  3. 创建下属

    1
    2
    3
    4
    5
    6
    class Subordinate {
    fun work(worker: String, callback: Callback<String>) {
    println("下属接到指令,开始处理工作${worker}") // 处理工作
    callback.callback("${worker}已完成")// 将任务完成结果反馈给上司
    }
    }

    下属类很简单,就只接受任务就行了(打工人冲!)

  4. 接下来就开始回调

    1
    2
    3
    4
    5
    fun main() {
    Boss(Subordinate()).run {
    arrangeWorker("扫地")
    }
    }

    打印结果为:

    1
    2
    3
    4
    上司开始安排任务
    上司下班^^
    下属接到指令,开始处理工作扫地
    上司收到通知:工作扫地已完成已完成

    上述代码可以看到异步回调是不会阻塞主线程的,上司在任务发布完之后就下班了,之后下属完成任务之后再通过另一个线程通知上司

地狱

既然要展示地狱,那肯定要再嵌套一层。设计如下

  1. 现在老板需要产品经理来设计产品并获得设计好的产品

    更改一下arrangeWorker里面的内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class Boss(private val subordinate: Subordinate) : Callback<String> {

    ...

    fun arrangeWorker(worker: String) {
    println("老板通知产品经理进行设计")
    Thread {
    subordinate.work(worker, this)
    }.start()
    println("老板下班^^")
    }
    ...
    }
  2. 通知完产品经理,收到回调后,那就得让开发来进行开发了嘛

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    class Boss(private val subordinate: Subordinate) : Callback<String> {

    override fun callback(value: String) {
    println("老板通知:产品经理已将${value}设计好了")
    val newWork = "$value 系统"
    println("产品经理设计完成,再将任务交给开发")
    Subordinate().work(newWork, object : Callback<String> {
    override fun callback(value: String) {
    println("开发:开发${value}完毕")
    }
    })
    }

    fun arrangeWorker(worker: String) {
    println("老板通知产品经理进行设计")
    Thread {
    subordinate.work(worker, this)
    }.start()
    println("老板下班^^")
    }
    }

    于是再老板的回调中,再创建一个开发并开一个新的回调

  3. 运行看一下把,输入Ios

    1
    2
    3
    4
    5
    6
    7
    老板通知产品经理进行设计
    老板下班^^
    下属接到指令,开始处理工作Ios
    老板通知:产品经理已将Ios设计好了
    产品经理设计完成,再将任务交给开发
    下属接到指令,开始处理工作Ios 系统
    开发:开发Ios 系统完毕

好啦,一个简单的回调地狱就完成了,其实回调地狱就是回调嵌套回调,但是如果嵌套层数过多,仿佛掉入地狱,于是有了回调地狱的说法。

优势

那这有啥优点嘛?

  • 优点还是有的:回调地狱给我们带来什么?事实上,回调的代码如同管道一样接收输入,并将处理后的内容输出至下一步。而回调地狱,则是多个管道连接,形成的一个流程,而各个子流程(管道)相互独立。

缺点

缺点很明显

  • 当然缺点很明显:回调的方法虽然将子过程解耦,但是回调代码的可读性降低、复杂性大大增加。看得我头晕,真的