首页 > 其他分享 >回调机制详解

回调机制详解

时间:2024-12-10 13:31:52浏览次数:11  
标签:异步 调用 CallBack 详解 Chen 机制 回调 方法

一、什么是回调

回调是一种双向的调用模式,程序模块之间通过这样的接口调用完成通信联系,回调的核心就是回调方将本身即this传递给调用方,这样调用方就可以在调用完毕之后再告诉回调方它想要知道的信息。

回调函数用于层间协作,上层将本层函数安装在下层,这个函数就是回调,而下层在一定条件下触发回调,例如作为一个驱动,是一个底层,他在收到一个数据时,除了完成本层的处理工作外,还将进行回调,它将这个数据交给上层应用层来做进一步处理,这在分层的数据通信中很普遍。其实回调和API非常接近,他们的共性都是跨层调用的函数。但区别是API是低层提供给高层的调用,一般这个函数对高层都是已知的;而回调正好相反,他是高层提供给底层的调用,对于低层他是未知的,必须由高层进行安装。这个安装函数其实就是一个低层提供的API,安装后低层不知道这个回调的名字,但它通过一个函数指针来保存这个回调,在需要调用时,只需引用这个函数指针和相关的参数指针。

其实:回调就是该函数写在高层,低层通过一个函数指针保存这个函数,在某个事件的触发下,低层通过该函数指针调用高层那个函数。从调用方式上看,可以分为两类:同步回调、异步回调。

通俗来说:

就是A类需要调用B类中的方法,然后B类方法执行结束后通过函数指针(A类传过来)调用A类中的回调方法。

回调机制使用场景:

在项目中,在支付宝的沙箱支付中就用到了回调机制。在支付Controller层中通过pay方法调用支付宝提供的API,跳转支付宝支付界面。

但是在支付接口中我们并不是等待支付宝支付页面支付完成才继续走下去即非阻塞式,而且我们在pay方法中也并不知道支付宝支付页面中的支付结果具体如何。那么我们该如何获取结果呢,就是通过支付宝的异步回调接口来通知我们结果。

在支付完成后支付宝会异步回调Controller层中的回调方法来通知结果。

二、同步回调与异步回调:

1、同步回调:

同步调用是一种阻塞式调用,是最基本并且最简单的一种调用方式,类A的方法a()调用类B的方法b(),一直等待b()方法执行完毕,a()方法才能继续往下走。这种调用方式适用于方法b()执行时间不长的情况,因为b()方法执行时间一长或者直接阻塞的话,a()方法的余下代码是无法执行下去的,这样会造成整个流程的阻塞。

例如上方支付宝回调场景,加入是同步机制回调,那么在pay方法中我们就需要等待支付宝支付结果,那假设网络等因素导致支付结果很慢还没有出来,那么我们的程序就一直阻塞在这里,那这样性能就会非常糟糕。

2、异步回调:

(1)异步调用是为了解决同步调用可能出现阻塞,导致整个流程卡住而产生的一种调用方式。类A的方法方法a()通过新起线程的方式调用类B的方法b(),代码接着直接往下执行,这样无论方法b()执行时间多久,都不会阻塞住方法a()的执行。但是这种方式,由于方法a()不等待方法b()的执行完成,在方法a()需要方法b()执行结果的情况下,必须通过一定的方式对方法b()的执行结果进行监听。为了完成这点,就需要另开一个线程了。

(2)异步调用在应用程序框架中具有广泛的应用,并且特指多线程情况下。它同Windows的消息循环机制,消息响应,消息队列,事件驱动机制以及设计模式中的观察者模式等都是紧密相关的。 在单线程方式下,计算机是一台严格意义上的冯·诺依曼式机器,一段代码调用另一段代码时,只能采用同步调用,必须等待这段代码执行完返回结果后,调用方才能继续往下执行。有了多线程的支持,可以采用异步调用,调用方和被调方可以属于两个不同的线程,调用方启动被调方线程后,不等对方返回结果就继续执行后续代码。被调方执行完毕后,通过某种手段通知调用方:结果已经出来,请酌情处理。异步回调常见于请求服务器数据,当取到数据时,会进行回调(例如支付宝支付回调)。

三、异步回调例子(JAVA):

上面讲了那么多,其实所谓回调,就是A类中调用了B类的某个方法C,然后B类反过来调用A类的方法D,D这个方法就叫回调方法。

Class A实现接口CallBack callback——背景1

class A中包含一个class B的引用b ——背景2

class B有一个参数为callback的方法f(CallBack callback) ——背景3

A的对象a调用B的方法 f(CallBack callback) ——A类调用B类的某个方法 C

然后b就可以在f(CallBack callback)方法中调用A的方法 ——B类调用A类的某个方法D

/**
 * java 异步接口回调
 */
public interface CallBack {

    /**
     * 这时一个回调接口
     * @param result
     */
    public void resultNotify(String result);
}

/**  相当于A类 有任务处理 需要调用B类中的方法进行处理
 * @author cg
 * @version 1.0
 * @Date 2024/4/20 15:33
 */
public class Chen implements CallBack{


    private Li li;

    public void setLi(Li li){
        this.li=li;
    }

    //调用Li类 进行任务处理
    public void ToTaskLi( String task){


        //另起线程模拟异步调用场景
        new Thread(new Runnable() {

            @Override
            public void run() {
                li.doTask(Chen.this,task);
            }
        }).start();
        //将任务交由类li处理  Chen类继续往下执行别的任务  无需阻塞
        playGame();
    }


    public void playGame(){
        System.out.println("Chen玩去了.......");
    }


    //异步回调函数
    @Override
    public void resultNotify(String result) {
        System.out.println("Chen的接口回调结果:"+result);
    }
}
/** 相等于任务处理类 处理完成后通知A类 并将结果告知
 * @author cg
 * @version 1.0
 * @Date 2024/4/20 15:33
 */
public class Li {
    
    public void doTask(CallBack callBack,String task){
        System.out.println("Li接受到任务:"+task);

        //模拟处理任务
        for (int i = 0; i < 100000; i++) {

        }
        System.out.println("Li类任务处理完毕......");
        String result="任务处理成功";
        //调用chen类的回调函数
        callBack.resultNotify(result);

    } 
}
/** 测试java异步回调
 * @author cg
 * @version 1.0
 * @Date 2024/4/20 15:43
 */
public class TestYiBuHuiDiao {


    public static void main(String[] args) {
        Chen chen=new Chen();
        Li li = new Li();
        chen.setLi(li);
        String task="拿取参数任务";
        chen.ToTaskLi(task);
    }
}

结果:

代码解析:

在这段代码中,Chen类是CallBack接口的实现类,而调用Li类的doTask()方法中传递了一个Chen.this参数(this关键字的使用),Chen.this相当于类Chen的实例对象(和Chen chen一样)。Li.doTask(CallBack callback)中参数要求是CallBack类型,因为Chen类实现了CallBack接口,所以它的实例可以作为CallBack类型的参数传递。

为什么使用CallBack callback作为参数?

这里就涉及到Java的接口和多态。因为CallBack是一个接口,任何实现了该接口的类都必须实现接口中定义的所有方法。所以只需要传入一个CallBack类型的引用就可以,而在Li类中不需要关心这个类指向的是哪个类的实例(在这里是指向的Chen类实例,也可以是各种实现该接口的实例),这就是用到了多态的概念。

那么如果写成li.doTask(Chen chen)可以吗?

这种方式当然也可以,但如果还有Wang、Liu.....等各种实现了CallBack接口的类同样也要调用这个方法呢。那么我是不是就要在li中定义doTask(Wang wang)、doTask(Li li).....各种方法。所以这里就可以看到多态提供的好处了。我们只需要定义接口类型即可,具体传入哪个是哪个类型无需关心。因为它绝对是这个接口的实现类。

标签:异步,调用,CallBack,详解,Chen,机制,回调,方法
From: https://blog.csdn.net/Tomkruse11/article/details/144350534

相关文章

  • Django 中的数据库update() 和 save() 方法的机制的不同
    1.save()方法•机制:•调用save()方法时,会先对模型实例的字段进行验证(如字段类型、长度约束等)。•如果模型定义了clean()或者字段设置了验证规则(如validators),这些规则也会被执行。•验证通过后,Django才会将数据保存到数据库中。•触发的逻辑:•触发模型的......
  • ECMAScript标准详解
    文章目录ECMAScript标准详解一、引言二、ECMAScript核心概念1、基本语法和结构1.1、变量声明1.2、函数2、数据类型和操作符2.1、字符串和模板字面量2.2、解构赋值三、ECMAScript高级特性1、异步编程1.1、Promises1.2、async/await四、使用示例1、模块化1.1、导出......
  • JDBC详解
    JDBC详解1、什么是JDBC,有什么作用,优缺点是什么?JDBC(JavaDatabaseConnectivity)是一个JavaAPI,它提供了一种标准的方法,允许Java程序连接到数据库并执行SQL语句。JDBC为不同类型的数据库提供了统一的访问方式,包括关系型数据库如MySQL、Oracle、SQLServer等。作用:数据库连......
  • 动态代理详解
    动态代理详解1、什么是代理模式  代理模式引用官方原文话来讲:代理模式通过引入一个代理对象来控制对原对象的访问。代理对象在客户端和目标对象之间充当中介,负责将客户端的请求转发给目标对象,同时可以在转发请求前后进行额外的处理。转化为生活中例子来讲(代购/秘书等),我们需要......
  • 不求甚解--详解ansible-playbook中roles的用法
    前言本文将详细介绍ansible-playbook中roles的各种用法,它允许你将相关的任务、变量、处理器、文件和模板等集合在一起,以便于在不同的项目中复用环境准备组件版本操作系统Ubuntu22.04.4LTSansible2.17.6基本用法文件结构.├──deploy.hosts├──dep......
  • 鸿蒙 Next 中 Prop 的用法详解
    一、@Prop概述在鸿蒙Next中,@Prop装饰器用于在父子组件之间建立单向的数据同步关系。这意味着数据从父组件流向子组件,子组件对@Prop装饰变量的修改不会同步回父组件。从APIversion9开始,该装饰器支持在ArkTS卡片中使用,从APIversion11开始,支持在元服务中使用。(一)同步机制父组......
  • 【信息系统项目管理师】第9章:项目范围管理过程详解
    文章目录一、规划范围管理1、输入2、工具与技术3、输出二、收集需求1、输入2、工具与技术3、输出三、定义范围1、输入2、工具与技术3、输出四、创建WBS1、输入2、工具与技术3、输出五、确认范围1、输入2、工具与技术3、输出六、控制范围1、输入2、工具与技术3、输......
  • 【Linux内核】详解从socket到epoll高效网络编程
    socket网络编程的步骤先给出大致流程:服务端:创建自己的socket连接再打开自己的可以用于通信的端口,并把自己的ip告诉要通信的客户端打开监听的socket,监听是否有客户端连接接受客户端的连接如果有客户端连接上来,则接收数据后,再回复不用于通信则关闭socket客户端:创......
  • C# Task异步同步机制
    1.引入第三方工具包:Nito.AsyncEx;AsyncLock:1usingMicrosoft.VisualBasic.Devices;2usingNito.AsyncEx;3usingSystem.Diagnostics;45namespaceWinFormsApp26{7publicpartialclassForm1:Form8{9publicForm1()10......
  • CCF GESP C++ 二级上机题(十六道题及其思路详解合集)
    #include<iostream>usingnamespacestd;intmain(){//定义一个整型变量n,用于接收输入的数值,该数值将决定后续循环的次数等操作intn;cin>>n;//定义两个循环变量i和j,分别用于外层循环和内层循环的计数inti,j;//定义字符变量s并初始化......