Flutter实现CombineExecutor进行多个异步分组监听,监听第一个异步执行的开始和最后一个异步执行结束时机。

news2024/11/26 4:54:10

1.场景

我们在调用接口时,很多时候会同时调用多个接口,接口都是异步执行,我们很难知道调用的多个接口哪个会最后执行完成,我们有时候需要对最后一个接口执行完成的时机监听,所以基于该需求,设计了CombineExecutor,对类似的需求进行监听。

2.代码

group_key.dart

///合并执行分类
class GroupKey {
  ///是否需要监听,不需要监听,则不会执行监听回调
  final bool isMonitor;

  GroupKey({this.isMonitor = true});
}

executor.dart

import 'group_key.dart';

///执行者
///开始一个无限循环的执行进程,等待事件默认50毫秒
class Executor {
  final GroupKey key;

  ///延迟时间。
  ///进程执行的快慢,单位毫秒ms,时间越短,反应越灵敏,
  ///但是消耗的新能越多,不能设置为0,否则会卡住进程。
  ///默认延迟50ms。
  final int? delayed;
  bool _stop = true;
  Function(GroupKey key)? _stopCallback;
  Function(GroupKey key)? _startCallback;

  Executor(this.key, {this.delayed});

  ///开始执行[Executor]
  ///[callback]会循环调用
  start({Function(GroupKey key)? callback}) async {
    _stop = false;
    _startCallback = callback ?? _startCallback;
    while (!_stop) {
      _startCallback?.call(key);
      await Future.delayed(Duration(milliseconds: delayed ?? 50));
    }
    _stopCallback?.call(key);
  }

  ///结束执行[Executor]
  ///[callback]只会在进程结束时执行一次
  stop({Function(GroupKey key)? callback}) {
    _stop = true;
    _stopCallback = callback ?? _stopCallback;
  }

  ///是否已启动
  bool isStart() {
    return !_stop;
  }

  @override
  int get hashCode => key.hashCode;

  @override
  bool operator ==(Object other) =>
      other is! Executor ? false : key == other.key;
}

monitor.dart

import 'package:kq_flutter_widgets/utils/str_util.dart';

///合并执行状态持有
class Monitor {
  dynamic extra;
  bool _isStart = false;
  bool _isFinish = false;
  bool _isError = false;

  Monitor({this.extra});

  ///该方法接口开始调用时调用
  @Deprecated("只需要监听完成,不需要监听开始,创建即开始")
  onStart() {
    _isStart = true;
  }

  ///该方法接口调用完成时调用
  onFinish() {
    _isFinish = true;
  }

  ///该方法接口出错或者请求失败时调用
  onError() {
    _isError = true;
  }

  ///该接口是否已开始调用
  @Deprecated("只需要监听完成,不需要监听开始,创建即开始")
  bool isStart() {
    return _isStart;
  }

  ///该接口是否已完成调用
  bool isFinish() {
    return _isFinish;
  }

  ///该接口是否调用出错
  bool isError() {
    return _isError;
  }

  ///获取额外数据
  T? getExtra<T>() {
    return StrUtil.getValue(extra);
  }

  ///重置,以便复用
  reset() {
    _isStart = false;
    _isFinish = false;
    _isError = false;
  }
}

str_util.dart

/// 字符串辅助类
class StrUtil {
  ///类型判断
  static T? getValue<T>(var value) {
    if (value == null) {
      return null;
    } else if (T == bool) {
      return (value == "1" || value == "true" || value is bool) as T;
    } else if (T == String) {
      return value as T;
    } else if (T == int) {
      return int.parse(value) as T;
    } else if (T == double) {
      return double.parse(value) as T;
    } else {
      return value;
    }
  }
}

combine_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';

///合并执行代码,主要用到接口调用上,
///只监测接口执行过程,不涉及接口回调参数等处理。
///以最开始执行的接口开始回调[onStart]方法,
///以最后执行完成的接口回调[onFinish]方法。
///当第一个回调开始了,并已回调完成了,表示整个接口执行完毕,
///当第一个接口执行完毕后,还没开始执行第二个接口,则即使他们有共同的[GroupKey],
///他们也不能在一个处理周期中处理,我们把一个同一个[GroupKey]下执行的[onStart]和[onFinish],
///表示一个处理周期。
class CombineExecutor {
  ///执行的对象保存
  final Map<GroupKey, List<Monitor>> _combines = {};

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

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

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

  _executor(GroupKey key) {
    Executor executor = Executor(key);
    if (!_executors.contains(executor)) {
      _executors.add(executor);
      onStart?.call(key);

      executor.start(callback: (key) {
        List<Monitor> combines = _getCombines(key);
        bool flag = true;
        for (Monitor combineMonitor in combines) {
          if (!combineMonitor.isFinish() && !combineMonitor.isError()) {
            flag = false;
            break;
          }
        }

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

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

  ///获取合并执行观察者,
  ///设置到请求逻辑中。
  Monitor getCombine(GroupKey key) {
    Monitor combineMonitor = Monitor();
    _addCombine(key, combineMonitor);
    _executor(key);
    return combineMonitor;
  }

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

  List<Monitor> _getCombines(GroupKey key) {
    if (_isEmptyCombine(key)) {
      return [];
    } else {
      return _combines[key]!;
    }
  }

  ///CombineMonitor是否为空
  _isEmptyCombine(GroupKey key) {
    return !_combines.containsKey(key) || _combines[key].isNullOrEmpty;
  }

  _clearCombine(GroupKey key) {
    _combines.remove(key);
  }

  ///清除全部的CombineMonitor
  _clearAllCombine() {
    _combines.clear();
  }
}

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

    ///创建对象
    CombineExecutor executor = CombineExecutor(
      onStart: (key) {
        ///print("执行了onStart");
      },
      onFinish: (key) {
        if (key == groupKey) {
          ///print("执行了onFinish");
        }
      },
    );

    ///获取CombineMonitor 传入到接口调用中
    Monitor monitor = executor.getCombine(groupKey);

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

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

3.使用

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

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

相关文章

反序列化漏洞复现(typecho)

文章目录 执行phpinfogetshell 执行phpinfo 将下面这段代码复制到一个php文件&#xff0c;命名为typecho_1.0-14.10.10_unserialize_phpinfo.php&#xff0c;代码中定义的类名与typecho中的类相同&#xff0c;是它能识别的类&#xff1a; <?php class Typecho_Feed{const…

IPv6改造深化之路

01 IPv6改造问题及整体改造思路 随着“十四五”期间国家政策对IPv6深化改造及规模部署的推动&#xff0c;在IPv6改造过程中出现了越来越多的系统性问题&#xff0c;如图1所示。 图1 关于IPv6改造的各种疑问所有跨设备通信的IT软硬件系统均需要处理IP地址&#xff0c;各领域均需…

LeetCode —— 复写零(双指针)

题目链接 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题目解析 将数组中出现的每个零复写一遍&#xff0c;然后将其他元素向右平移&#xff0c;数组长度不能改变。 法一&#xff1a;使用额外空间的做法 class Solution { public:void duplica…

以指标管理为抓手,亿信华辰助力锦州银行打造全行级数据应用

随着金融数字化转型的持续深入&#xff0c;数据智能的业务化应用等正逐步受到行业关注。锦州银行携手亿信华辰&#xff0c;开展全行级指标管理体系建设&#xff0c;实现指标数据统一管理、统一标准、统一来源、统一汇总、统一加工、统一呈现&#xff0c;以及持续推动基础数据治…

Day56|动态规划part16:647. 回文子串、516. 最长回文子序列、动态规划总结篇

647. 回文子串 leetcode链接&#xff1a;力扣题目链接 视频链接&#xff1a;动态规划&#xff0c;字符串性质决定了DP数组的定义 | LeetCode&#xff1a;647.回文子串 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。回文字符串 是正着读和倒过…

Mysql--技术文档--索引-《索引为什么查找数据快?》-超底层详细说明索引

索引的概念 在MySQL中&#xff0c;索引是一种数据结构&#xff0c;它被用于快速查找、读取或插入数据。索引能够极大地提高数据库查询的速度。 索引的工作方式类似于图书的索引。如果你想在图书馆找到一本书&#xff0c;你可以按照书名进行查找。书名就像是一个索引&#xf…

0012Java程序设计-springboot基于微信小程序的校园智慧帮系统的设计与实现

摘要目录相关技术2.1 MySQL数据库2.2 SpringBoot框架2.3 uniapp框架2.4 B/S架构 系统设计系统实现开发环境 摘要 随着移动互联网高速发展&#xff0c;手机、移动智能终端设备在生活中有着越来越重要的地位。在高校推崇以人为本的今天&#xff0c;也逐渐重视“移动互联网”技术…

微信小程序集成腾讯im,会话列表数据过多(长列表),卡顿问题的解决

说明 我这边用小程序集成im&#xff0c;然后结合公司的需求&#xff0c;做了一个聊天的小程序&#xff0c;在测试上线的时候没有问题&#xff0c;结果到客户那边&#xff0c;因为他们聊天的人多&#xff0c;会话列表达到了300多条&#xff0c;然后点击会话列表&#xff0c;进入…

【字符串匹配】暴力匹配算法

​ 一、暴力匹配算法原理 暴力匹配算法&#xff0c;也称为朴素字符串匹配算法&#xff0c;是一种简单但不高效的字符串匹配方法。它的原理非常直观&#xff0c;其主要思想是逐个字符地比较文本串和模式串&#xff0c;从文本串的每个可能的起始位置开始&#xff0c;依次检查是…

ESD实时监控监测系统通常包括哪些功能

ESD实时监控监测系统是一种用于监测和控制静电放电的系统。静电放电&#xff08;Electrostatic Discharge&#xff0c;ESD&#xff09;是指由于电荷的不平衡而引起的突发放电现象&#xff0c;可能对电子元器件、设备和工作环境造成损害。 ESD实时监控监测系统通常包括以下功能…

6000+药品靶点在研数据库-<查询工具推荐>

了解在研药物靶点数据对于药物研发、靶点发现和验证、药物安全性评估以及治疗策略优化都具有重要的意义&#xff0c;可以为科学家提供有价值的信息和指导。如在研药物靶点数据为药物研发提供了重要的指导。了解当前正在研究的药物靶点可以帮助科学家了解当前研究的热点领域&…

九州未来入选“AIGC算力产业全景图”

日前&#xff0c;量子位智库发布《AIGC算力全景与趋势报告》&#xff08;以下简称报告&#xff09;&#xff0c;通过广泛调研与深度分析&#xff0c;系统性分析了AIGC算力构成、产业链条&#xff0c;进一步指出了AIGC算力的五新趋势及三大阶段发展预测。其中&#xff0c;九州未…

华为云云服务器评测|初始化配置SSH连接 安装MySQL的docker镜像 安装redis以及主从搭建 7.2版本redis.conf配置文件

目录 引出初始化使用&#xff0c;SSH连接控制台设置密码和配置开放连接的端口在finalshell中建立连接 安装docker&#xff0c;运行MySQL安装docker拉取运行mysql容器 redis的拉取运行redis.conf的配置&#xff08;7.2.0版本&#xff09;准备挂载文件和运行redis的主从搭建&…

企业如何建设主数据管理体系?这篇文章说清楚了

主数据是企业核心的基本业务数据&#xff0c;数据长期存在且应用于多个系统(ERP系统、MES系统、OA系统等)系统内的编码数量成几十万个&#xff0c;并且在不断增长。由于缺乏统一的标准规范&#xff0c;各系统内由于实施商不同、使用单位不同&#xff0c;同数据在各系统内编码不…

8851-LC-MT GE 具有便于现场布线的螺丝端子

8851-LC-MT GE 具有便于现场布线的螺丝端子 例如,两个UDS-TCS提供了与基于PCI的解决方案相同的功能,该解决方案采用16通道板、螺杆终端外壳和屏蔽电缆,但成本将减少247美元(29%)。相对于镍公司基于usb的热电偶产品(usb-9211),USB-TC提供了两倍的通道,但成本降低了24%。 支持…

VB(Visual Basic)程序设计

一&#xff1a;前言 1.发展方向 1.1 学习方向 Web前端学习路线 Java学习路线 嵌入式开发学习路线 如何知道企业的需求技术&#xff1a;去招聘软件看企业的要求 前端、后端、测试、运维、UI、网络安全、游戏开发..... 1.2 学习平台 程序员视频学习平台 1.3 计算机学习资源 …

【C++漂流记】结构体的定义和使用、结构体数组、结构体指针、结构体做函数参数以及结构体中const的使用

结构体&#xff08;struct&#xff09;是C语言中一种重要的数据类型&#xff0c;它由一组不同类型的成员组成。结构体可以用来表示一个复杂的数据结构&#xff0c;比如一个学生的信息、一个员工记录或者一个矩形的尺寸等。 结构体定义后&#xff0c;可以声明结构体变量&#xf…

大数据Flink(七十三):SQL的滚动窗口(TUMBLE)

文章目录 SQL的滚动窗口(TUMBLE) SQL的滚动窗口(TUMBLE) 滚动窗口定义:滚动窗口将每个元素指定给指定窗口大小的窗口。滚动窗口具有固定大小,且不重叠。例如,指定一个大小为 5 分钟的滚动窗口。在这种情况下,Flink 将每隔 5 分钟开启一个新的窗口,其中每一条数都会划…

kubernetes——ingress

简介 ingress: 是k8s内部的一个资源对象ingress controller -> ingress控制器&#xff1a; 是k8s里启动的一个pod&#xff0c;运行的是nginx的镜像&#xff0c;实现k8s内部的service&#xff08;ClusterIP类型&#xff09;的负载均衡 ingress 和ingress controller 的关…

孙哥Spring源码第18集

第18集 refresh()-invokeBeanFactoryPostProcessor-二-ConfigurationClassPostProcessor的处理逻辑 【视频来源于&#xff1a;B站up主孙帅suns Spring源码视频】【微信号&#xff1a;suns45】 1、为什么PropertySource先处理&#xff1f; 因为Conponent A在处理的过程中 要把…