Flutter实现ControlExecutor进行多个异步任务执行时监听状态并可指定最后执行的异步并在指定的异步执行完毕后结束executor并回调。

news2024/10/7 14:33:47

1.场景

当有多个接口请求时,且接口调用不是同时进行时,而且接口调用有可能时链式的,中间也有可能加入别的逻辑,但是需要在第一个接口调用时打开等待框,在最后一个接口调用完成时关闭等待框类似需求时,可以用到ControlExecutor进行接口执行过程的监听,并可标记最后一个执行的接口,且会等待做了标记的接口完成执行后,关闭执行,并执行onFinish回调。

2.代码

其中executor.dart、group_key.dart、minitor.dart、ke_ex.dart跟上一篇文章CombineExecutor中的一样,请去上一篇文章中去拷贝。

control_executor.dart

import 'package:kq_flutter_widgets/utils/ex/kq_ex.dart';

import 'core/executor.dart';
import 'core/group_key.dart';
import 'core/monitor.dart';

///可控制执行结束时机的执行者。
///主要用于多个接口联级调用或者链式调用等场景Loading框的控制。
///跟CombineExecutor原理类似,只是ControlExecutor可控制,
///不会自动自行完毕,需要用户主动调用stop或者给需要执行完毕的接口添加stop标记。
///可以监听多个接口联级调用、链式调用、多接混合调用出现异常和报错等监听,
///不干涉接口请求逻辑,只监听接口请求的
///成功状态(response.code == ApiResponse.success),失败状态(response.code != ApiResponse.success),
///接口catch后的监听,以及onError监听,例如在调用一系列接口时,
///调用第一个接口之前会回调onStart,
///等调用到接口状态设置了stop=true时的接口时和中间任意一个接口接口异常或失败状态时,
///则会回调onFinish,结束本次执行。
class ControlExecutor {
  ///执行的对象保存
  final Map<GroupKey, List<Monitor>> _monitors = {};

  ///Executor 保存
  final List<Executor> _executors = [];

  final Function(GroupKey key)? onStart;
  final Function(GroupKey key)? onFinish;

  ControlExecutor({this.onStart, this.onFinish});

  ///这里的逻辑跟CombineExecutor不同。
  _executor(GroupKey key) {
    Executor executor = Executor(key);
    if (!_executors.contains(executor)) {
      _executors.add(executor);
      onStart?.call(key);

      executor.start(callback: (key) {
        List<Monitor> combines = _get(key);
        bool flag = false;
        for (Monitor monitor in combines) {
          if (monitor.isError() ||
              monitor.isFinish() && (monitor.getExtra() ?? false)) {
            flag = true;
            break;
          }
        }

        //表示最后一个都已执行完成
        if (flag) {
          executor.stop(callback: (key) {
            onFinish?.call(key);
            _clear(key);
            _executors.remove(executor);
          });
        }
      });
    }
  }

  ///停止,在退出界面时调用
  stop() {
    for (Executor executor in _executors) {
      executor.stop();
    }
    _executors.clear();
    _clearAll();
  }

  ///获取合并执行观察者,
  ///设置到请求逻辑中。
  ///[stop] 是否停止。
  Monitor getMonitor(GroupKey key, {bool? stop}) {
    Monitor monitor = Monitor(extra: stop);
    _addCombine(key, monitor);
    _executor(key);
    return monitor;
  }

  ///新增一个monitor
  _addCombine(GroupKey key, Monitor combine) {
    if (key.isMonitor) {
      if (_monitors.containsKey(key)) {
        List<Monitor>? combines = _monitors[key];
        combines ??= [];
        if (!combines.contains(combine)) {
          combines.add(combine);
        }
      } else {
        _monitors.putIfAbsent(key, () => [combine]);
      }
    }
  }

  List<Monitor> _get(GroupKey key) {
    if (_isEmpty(key)) {
      return [];
    } else {
      return _monitors[key]!;
    }
  }

  ///monitor是否为空
  _isEmpty(GroupKey key) {
    return !_monitors.containsKey(key) || _monitors[key].isNullOrEmpty;
  }

  _clear(GroupKey key) {
    _monitors.remove(key);
  }

  ///清除全部的monitor
  _clearAll() {
    _monitors.clear();
  }
}

///测试
class ControlExecutorTest {
  test() {
    ///创建一个GroupKey,改key可用于一组需要调用的接口上
    GroupKey groupKey = GroupKey();

    ///创建对象
    ControlExecutor executor = ControlExecutor(
      onStart: (key) {
        ///print("开始执行");
      },
      onFinish: (key) {
        if (key == groupKey) {
          ///print("结束执行");
        }
      },
    );

    ///获取monitor 传入到接口调用中
    Monitor monitor1 = executor.getMonitor(groupKey);
    ///获取monitor 传入到接口调用中
    Monitor monitor2 = executor.getMonitor(groupKey);
    ///获取monitor 传入到接口调用中,结束的monitor
    Monitor monitor3 = executor.getMonitor(groupKey, stop: true);

    ///模拟异步对Monitor进行操作
    Future.delayed(const Duration(seconds: 2), () {
      monitor1.onFinish();
    });
    Future.delayed(const Duration(seconds: 3), () {
      monitor2.onFinish();
    });
    Future.delayed(const Duration(seconds: 5), () {
      monitor3.onFinish();
    });

    ///退出界面
    executor.stop();
  }
}

3.使用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/976781.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spark 6:Spark SQL DataFrame

SparkSQL 是Spark的一个模块, 用于处理海量结构化数据。 SparkSQL是用于处理大规模结构化数据的计算引擎 SparkSQL在企业中广泛使用&#xff0c;并性能极好 SparkSQL&#xff1a;使用简单、API统一、兼容HIVE、支持标准化JDBC和ODBC连接 SparkSQL 2014年正式发布&#xff0c;当…

java调用js文件的两种方法(支持V8引擎)

前言 对java逆向感兴趣的盆友可以关注我以前的文章&#xff0c;有图片验证码识别、AES、SHA256等各种加密的java实现&#xff0c;不定时更新常用算法和加密&#xff0c;欢迎一起交流讨论&#xff01; 在日常逆向中&#xff0c;一些前端的加密代码用java复现出来比较难&#xff…

使用工作流快速开发平台,做好企业数据资源管理!

面对越来越繁忙的业务&#xff0c;很多客户朋友不知道用什么样的平台才能快速处理好企业内部数据&#xff0c;做出更有利于企业发展的经营决策。其实&#xff0c;低代码技术平台、工作流快速开发平台是可以让企业减少重复工作&#xff0c;提高工作效率&#xff0c;实现流程化办…

中断处理原理:接口及按键驱动

一、什么是中断 一种硬件上的通知机制&#xff0c;用来通知CPU发生了某种需要立即处理的事件 分为&#xff1a; 内部中断 CPU执行程序的过程中&#xff0c;发生的一些硬件出错、运算出错事件&#xff08;如分母为0、溢出等等&#xff09;&#xff0c;不可屏蔽外部中断 外设发…

自动计算比例 计算属性 computed @input=“rate“

<el-col :span"12"><el-form-item label"当年累计实收租金:" prop"cumulativeRent"><el-inputv-model"createForm.cumulativeRent"input"rate"clearable:disabled"value 2"><template slot…

如何将MySQL中指定的表结构同步到人大金仓数据库

场景 刚开始做数据库适配的时候,这是一个棘手的问题,因为MySQL的库里,表结构,字段都是最新的,但是金仓的库,全是旧版本的表结构。需要把我们模块的表结构,同步到金仓中。 虽然金仓有数据库同步工具,但是直接把所有表都给同步过来,难免会影响到其他模块。 然后…

HCIP学习-IPv6

目录 前置学习内容 IPv6解决的一些IPv4的缺陷 无限的地址 层次化的地址结构 即插即用 简化报文头部 IPv4和IPv6报头比较 端到端的网络罗完整性 安全性增强 挣钱QoS特性 IPv6地址介绍 格式 首选格式 压缩格式 内嵌IPv4地址格式的IPv6地址格式 IPv6的网络前缀和接…

TRICONEX 8312 数字输入模块

Triconex 8312 数字输入模块是一种用于工业自动化和控制系统的模块&#xff0c;通常用于监测和采集数字信号。以下是Triconex 8312 数字输入模块的一些常见产品特点&#xff1a; 多通道输入&#xff1a;8312 模块通常具有多个数字输入通道&#xff0c;允许同时监测多个数字信号…

【Linux】多线程2——线程互斥与同步/多线程应用

文章目录 1. 线程互斥1.1 问题引入1.2 线程互斥的相关概念1.3 互斥量mutex1.4 互斥量实现原理1.5 死锁 2. 线程安全和可重入函数3. 线程同步3.1 同步概念3.2 条件变量 4. 生产消费模型4.1 基于阻塞队列的cp模型4.2 基于环形队列的cp模型POSIX信号量 5. 线程池5.1 互斥量RAII版本…

总结/笔记-vue中的插槽(默认插槽、具名插槽、作用域插槽)

问题&#xff1a; 遇到了一个插槽&#xff0c;写法为 #default ”{ row }“ 插槽知识点&#xff1a; 定义 插槽&#xff0c;用于 在组件中 引用外部组件或自定义组件的内容。 即 子组件中提供给父组件使用的一个占位符&#xff0c;父组件可以在这个占位符中填充任何模板代…

【TI毫米波雷达笔记】ADCBuf外设初始化配置及驱动(以IWR6843AOP为例)

【TI毫米波雷达笔记】ADCBuf外设初始化配置及驱动&#xff08;以IWR6843AOP为例&#xff09; ADCBuf是为mmwave服务的 在配置之前需要配置好mmwave #include < ti/drivers/ADCBuf.h>对应mmwave studio&#xff1a; ADCBuf模块上电 调用&#xff1a; ADCBuf_init();A…

bash: cmake: command not found...+++++++lsb_release: command not found

一 .bash: cmake: command not found… centos中安装那个cmake。 1、问题 [rootPC3 home]# cmake bash: cmake: command not found... Similar command is: make当前系统&#xff1a; [rootPC3 home]# lsb_release -a LSB Version: :core-4.1-amd64:core-4.1-noarch:cxx…

dex2oat编译模式、触发场景、命令强制执行

dex2oat简单理解就是把delvik虚拟机的可执行文件dex转化成AndroidRuntime虚拟机的可执行文件oat。 Android T版本由PKMS下发命令、native层进程installd负责具体执行dex2oat操作。installd回去调用dex2oat64完成编译工作&#xff0c;可以将dex2oat64理解成一个程序。源码路径&…

本地使用GFPGAN进行图像人脸修复

人脸修复 1.下载项目和权重文件2.部署环境3.下载权重文件4.运行代码5.网页端体验 首先来看一下效果图 1.下载项目和权重文件 https://github.com/iptop/GFPGAN-for-Video.git2.部署环境 根据README文件部署好环境&#xff0c;额外还需要&#xff1a; cd GFPGAN-1.3.8 pyt…

在Qt创建的UI中放一个显示点云的窗口(PCL+QT5)

1、首先在Qt Designer创建UI后&#xff0c;拖一个Widget窗口出来 2、在对象查看器中右击该Widget&#xff0c;选择提升窗口部件&#xff0c;如下操作&#xff1a; 3、把UI转出来放在VS项目中&#xff0c;其中你的UI代码头文件会自带QVTKOpenGLNativeWidget.h&#xff0c;当然你…

企业专题片的优势

企业专题片可以通过生动、感人的方式传达企业形象和信息&#xff0c;引起观众的共鸣和兴趣。它是一种强有力的营销工具&#xff0c;能够提升品牌形象、增加产品或服务的认知度&#xff0c;并在激烈的市场竞争中突显企业的实力和成果。企业专题片具有多个好处和影响&#xff0c;…

【C语言】入门——数组

目录 ​编辑 1.一维数组的创建和初始化 1.1一维数组的创建&#xff1a; 1.2 一维数组的初始化 &#xff1a; 2.二维数组的创建的初始化 2.1二维数组的创建&#xff1a; 2.2二维数组的初始化&#xff1a; 3.数组越界 4.数组的使用 4.1一维数组的使用&#xff1a; 4.2二维…

1、PostgreSQL数据库的历史和特性简介

PostgreSQL是一个开源的关系型数据库管理系统&#xff08;DBMS&#xff09;&#xff0c;它具有强大的功能和广泛的可扩展性&#xff0c;被广泛用于各种规模的应用程序和项目中。 一、PostgreSQL 的发展历史 PostgreSQL数据库的来历可以追溯到20世纪80年代末和90年代初。最早由…

MyBatisPlus入门篇1 - 入门案例、CRUD开发、Lombok、分页查询

目录 MyBatisPlus是基于MyBatis框架基础上开发的增强型工具&#xff0c;旨在简化开发、提高效率。 1.入门案例 ①创建SpringBoot项目&#xff0c;添加Spring Web和Mysql driver ②手动在pom.xml文件中添加mybatis plus和druid依赖 <dependency><groupId>com.b…

攻防世界-Get-the-key.txt

原题 解题思路 notepad看到&#xff0c;这应该是一个压缩包&#xff0c;解压。 但是解压的时候提示格式不对&#xff0c;不是zip&#xff0c;rar可以。解压出来有一个key.txt&#xff0c;打开就行。