什么是回调?

2023-11-04

定义

天天说回调回调,真的了解吗?
消息异步为什么要传入回调接口,真的了解了吗?
前端 axios 发送请求写的箭头函数全都是回调函数,真的理解了吗?
其实回调很简单,就是比如 A 中的方法 a1() 调用了 B 中的方法 b1(),然后 b1() 方法执行完后(一般是执行完)再调用 a1() 方法传入的回调接口的回调方法将结果返回。回调方法一般都定义在接口当中,通用性更强。
那么肯定会有人说,直接通过 b1() 方法返回不就行了,为啥还要去搞什么回调去返回,这不脱裤子放屁?
其实不尽然,如果 a1() 方法调用 b1() 方法,b1() 方法阻塞了,一直重试,那么 a1() 方法也就无法继续向下执行,这种情况不是我们想要见到的。
一般使用回调方法的场景就是调用的方法十分耗时,或者追求效率,不太在意调用方法的结果。因此调用方不会去等,采用异步调用的方式,然后将回调接口通过方法传给被调用方。被调用操作执行完后,再调用接口中的回调方法通知调用方。
在这里插入图片描述

多说无益,举几个例子。

举例

场景一

现在有这么一个场景:老师上课提问学生,学生一时半会想不出来,老师不会一直等,而是转而问其他同学其他问题。随后该学生想出来了,告诉老师结果。
现在来编码实现一下。
先定义一个回调接口 CallBack

/**
 * @author zxb 2022/9/23 20:43
 */
public interface CallBack {

    /**
     * 回调接口
     * @param answer 收到的答案
     */
    public void receive(String answer);

}

再定义一个 Teacher 类,teacher 类需要关联 student 类,因为要调用 student 的方法。并且调用时开启了一个新的线程,体现了异步调用。

package com.zxb.callback;

import lombok.extern.slf4j.Slf4j;

/**
 * @author zxb 2022/9/24 8:49
 */
@Slf4j
public class Teacher implements CallBack {

    private Student student;

    public Teacher() {
    }

    public Teacher(Student student) {
        this.student = student;
    }


    public void askQuestion(String question, CallBack callBack) {
        log.info("听好了,我的问题是:" + question);
        new Thread(() -> student.thinkQuestion(question, callBack)).start();
        try {
            // 这个睡眠只是为了日志打印更好看
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("那你先想,我问其他同学问题。");
    }


    @Override
    public void receive(String answer) {
        if ("2".equals(answer)) {
            log.info("答对了");
        } else {
            log.info("答错了");
        }
    }
}

最后定义一个 Student 类,这里模拟学生想了 5 秒,然后调用 Teacher 传入的回调接口的回调方法告诉老师答案。

package com.zxb.callback;

import lombok.extern.slf4j.Slf4j;

/**
 * @author zxb 2022/9/24 8:49
 */
@Slf4j
public class Student {


    public void thinkQuestion(String question, CallBack callback) {
        log.info("老师" + question + "有点难,我先站着想想。");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("老师我想到了");
        callback.receive("2");
    }

}

测试
public class CallbackTest {
    public static void main(String[] args) {
        Student student = new Student();
        Teacher teacher = new Teacher(student);
        teacher.askQuestion("1 + 1 等于几", teacher);
    }
}

在这里插入图片描述

分析
这里老师问学生问题,学生要想很久,老师不想影响上课进度,故让学生自己先想,然后老师继续问其他同学问题,但是老师需要留一个口子给该学生,能让该学生通知自己,这是基于代码方面的思想。而这个留给学生通知自己的方式就是回调接口中的回调方法。
在这个例子中,Teacher 作为调用方自身实现了回调接口,重写了回调方法,老师实现了自己的逻辑,传给同学的回调接口实际上就是自己本身,然后学生调用老师重写的回调方法得以通知老师。
而调用方也可以不实现回调接口,这个回调接口由用户自己指定,比如场景二。

场景二

现在又有个场景,生产者发送消息,消息存放在消息中心,并且为了提高生产效率,生产者发完消息也先不管发送成功还是失败,而是继续向下走其他逻辑,只是给消息中心提供一个回调接口用于告知自己发送消息的结果。
那么现在来编码实现下。先定义一个回调接口。

package com.zxb.callback;

/**
 * @author zxb 2022/9/24 17:19
 */
public interface SendCallback {

    /**
     * 发送成功
     * @param sendResult 结果
     */
    public void onSuccess(SendResult sendResult);

    /**
     * 发送失败
     * @param exception 失败信息
     */
    public void onFail(Exception exception);

}

再定义一个生产者。

package com.zxb.callback;

import lombok.extern.slf4j.Slf4j;

/**
 * @author zxb 2022/9/24 17:31
 */
@Slf4j
public class Producer {

    final private MsgCenter msgCenter = new MsgCenter();

    public void send(String msg, SendCallback callback) {
        log.info("开始发送消息");
        new Thread(() ->{msgCenter.receiveMsg(msg, callback);}).start();
        log.info("执行其他逻辑");
    }

}

定义消息中心。

package com.zxb.callback;

import java.util.Date;

/**
 * @author zxb 2022/9/24 17:39
 */
public class MsgCenter {

    public void receiveMsg(String msg, SendCallback sendCallback){
        try {
            // 模拟延迟
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (msg == null || msg.length() == 0) {
            sendCallback.onFail(new Exception("消息不能为空"));
        } else if (msg.contains("窝草")) {
            sendCallback.onFail(new Exception("此条消息被和谐"));
        } else {
            SendResult sendResult = new SendResult();
            sendResult.setStatus(SendResult.Status.ACK);
            sendResult.setMsg(msg);
            sendResult.setSendDate(new Date());
            sendCallback.onSuccess(sendResult);
        }
    }

}

定义发送结果类:

package com.zxb.callback;

import lombok.Data;
import lombok.Getter;

import java.util.Date;

/**
 * @author zxb 2022/9/24 17:20
 */
@Data
public class SendResult {
    private Status status;
    private String msg;
    private Date sendDate;
    
    public enum Status {
        ACK(1, "成功"),
        NACK(-1, "失败");
        @Getter
        private Integer flag;
        @Getter
        private String msg;
        private Status(Integer flag, String msg) {
            this.flag = flag;
            this.msg = msg;
        }

        @Override
        public String toString() {
            return "Status{" +
                    "flag=" + flag +
                    ", msg='" + msg + '\'' +
                    '}';
        }
    }
}

测试:

package com.zxb.callback;

/**
 * @author zxb 2022/9/24 17:49
 */
public class SendTest {
    public static void main(String[] args) {
        Producer producer = new Producer();
        producer.send("Hello World", new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                System.out.println("结果为" + sendResult);
            }

            @Override
            public void onFail(Exception exception) {
                System.out.println(exception.getMessage());
            }
        });
    }
}

在这里插入图片描述

分析
生产者生产消息并发送,我们可以理解为发送异步消息,生产者无需等待消息中心给我们返回 ACK,而是继续走下面的逻辑或者继续发送消息。这里生产者发送消息调用了消息中心的 receiveMsg 方法,并将方法和回调接口传给了消息中心,消息中心就可以通过这个回调接口来通知生产者消息发送的情况。
这里生产者不是回调接口的实现类,回调接口的实现类通过匿名内部类的方式实现,由用户指定,形参通过消息中心回调传过来,然后生产者可以根据结果做一些其他的判断等操作。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

什么是回调? 的相关文章

随机推荐

  • 阿里云云服务器ECS要怎么绑定域名?

    阿里云云服务器ECS要怎么绑定域名 使用阿里云云服务器ECS 可以轻松地将域名与您的服务器实例进行绑定 以便通过域名来访问您的网站或应用程序 本文将提供详细的步骤和说明 教您如何在阿里云上绑定域名到ECS实例 一 购买域名 首先 在绑定域名
  • 目标检测——Anchor-Based算法的学习笔记

    1 前言 在写这篇笔记之前 首先我们需要确定的是 Anchor Based的思路一定优于Anchor Free的算法 这是毋庸置疑的 你想想 一个事情分成两步做 正负目标分类 目标的精细定位和回归 一个事情一步做 目标分类和回归 负样本被当
  • PixelStreaming数据通信

    PixelStreaming数据通信 简介 H5到UE4通信 H5发送 UE4接收 UE4到H5通信 UE4发送 H5接收 iframe postMessage 父页面 子页面 改造UE4提供的像素流送H5 UE4项目bat启动器 问题 U
  • MySQL生产环境部署架构

    MySQL生产环境部署架构 常用的分库分表架构 按业务id分库分表 建立索引映射表同时进行分库分表 数据同步到ES做复杂搜索 分库分表下如何分页 假设用户现在要查询自己的订单 同时订单要求要支持分页 该怎么做 方案一 因为同一个用户的订单可
  • 黑客突破防火墙常用的几种技术(转)

    一 防火墙基本原理 首先 我们需要了解一些基本的防火墙实现原理 防火墙目前主要分包过滤 和状态检测的包过滤 应用层代理防火墙 但是他们的基本实现都是类似的 路由器 网卡 防火墙 网卡 内部网络 防火墙一般有两个以上的网络卡 一个连到外部 r
  • 使用SSM框架进行Crud时后台出现javax.validation.UnexpectedTypeException: HV000030: No validator could be found

    1 处理之前的效果 2 原因是 Java实体类中属性是Integer类型 用了 NotBlank判断不能为空 而这个注解是判断字符串是否为空 3 解决办法 去掉 NotBlank注解 使用 NotNull 两者的区别 1 NotNull 适
  • ClassLoader.getSystemResource("") 为空的原因

    在idea自带的tomcat中 运行正常 发布到tomcat下获取为空 现修改成类 class getClassLoader getResource 获取方式
  • Vue整体架构分解

    Vue js的整体架构可以分解为以下几个部分 文章目录 1 数据驱动 2 组件化 3 响应式系统 4 虚拟DOM 5 插件系统 6 单文件组件 7 模板编译 总结 1 数据驱动 Vue的一个核心特点是数据驱动 Vue会在初始化的时候给数据提
  • 【image】src相对地址的引入

    src相对地址的引入 前言 相对路径 src引用 前言 在引入图片的时候 突然发现通过相对地址引入项目内图片不会了 特此在这里记录一下 以便学习 相对路径 使用原因 对于放在一个项目包内的图片资源 通过相对路径可直接引用 绝对路径是对于网络
  • ASP.NET Core 8 的 Web App

    Web App Web App 与 Web API 的不同之处在于包含 UI 部分 所谓的 UI 就是 HTML 页面 Web App 支持几种渲染HTML 的方式 服务端渲染 客户端渲染 混合渲染 服务端渲染 服务端渲染UI是在浏览器请求
  • Spyder闪退的解决过程记录

    安装了高版本的 PYQT5 后 原来安装的 老版本的 spyder打开闪退 考虑是版本不兼容的问题 卸载了spyder 重新安装了最新版 Version 5 3 1的spyder 在anaconda prompt中输入如下命令 pip un
  • 学习安卓应用开发一课一得+计算器

    一 在学习安卓应用开之前要掌握语言 本文章以Java为基础的安卓应用开发 二 Android开发环境配置 三 计算器开发 一 在学习安卓应用开之前要掌握语言 本文章以Java为基础的安卓应用开发 1 在学习Android开发之前 我首先需要
  • 网络编程(附代码)

    网络编程 1 网络通信协议 1 1 协议和七层模型 七层模型 也称为OSI Open System Interconnection 参考模型 是国际标准化组织 ISO 制定的一个用于计算机或通讯系统间互联的标准体系 它是一个七层的 抽象的模
  • MIPI信号的分析--结合示波器实际测试波形

    MIPI已经不陌生了 对于现在的设计中 摄像头接口 显示接口都有MIPI 所以有了CSI和DSI接口 MIPI最早是手机里面的协议 为了形成行业统一标准 MIPI联盟发起MIPI 移动行业处理器接口 作为移动应用处理器制定的开放标准 1 M
  • 深度学习—卷积神经网络(Convolutional Neural Networks)

    卷积神经网络 Convolutional Neural Networks 卷积神经网络 convolutional neural network CNN 是一种专门用来处理具有类似网格结构的数据的神经网络 例如时间序列数据 可以认为是在时间
  • sqli-labs通关详解

    sqli labs通关详解 Less1 Less1sqlmap运用 Less2 Less2sqlmap运用 Less3 sqlmap运用 Less4 sqlmap一把梭 Less5 sqlmap直接梭 Less6 Less7 Less8 s
  • Java对象转换最佳方案

    系统变的复杂 系统的层次划分越来越细 边界也越来越明确 然后每一层之间一般都有自己要处理的领域对象 统称为pojo一般在model或者domain包下 类的后缀不能为pojo 常见的一些模型类型 PO DO 持久层对象 一般和数据库直接打交
  • C语言丨快速排序法

    程序员在程序设计时常常需要对存储在数组中的大量数据进行处理 如排序 查找等 排序是把一系列无序的数据按照特定的顺序 如升序或降序 重新排列为有序序列的过程 对数据进行排序是最重要的应用之一 实际生活中的很多问题都需要对数据进行排序 之前我们
  • 多dex合并进apk

    批量重命名 选中所有 dex结尾的文件 右键批量重命名 Zip命令处理APK 删除原有classes dex zip d demo apk classes dex 添加新的脱壳classes dex zip m demo apk class
  • 什么是回调?

    定义 天天说回调回调 真的了解吗 消息异步为什么要传入回调接口 真的了解了吗 前端 axios 发送请求写的箭头函数全都是回调函数 真的理解了吗 其实回调很简单 就是比如 A 中的方法 a1 调用了 B 中的方法 b1 然后 b1 方法执行