Flutter 队列任务的实现

作者:BlackDream 时间:2023-07-07 17:25:14 

前言

在电商的应用中,最常见的就是在首页或完成某事件之后,弹出一堆的活动/广告。假如重叠弹出,很丑,给用户的体验也不好,所以一般都会依次依条件的弹出。

下面讲讲我是怎么实现一个方便的队列任务管理

队列

任务队列,那当然要有个队列。这个队列的任务内容应该是返回FutureFunction,因为我需要得到他处理完成的结果,比如等待弹窗关闭时return,才表示这个任务被完成。

typedef TaskEventFunction = Future Function();
class TaskQueue {
   List<TaskEventFunction> _actionQueue = [];
}

添加任务进队列

然后是加入队列的方法add

void add(TaskEventFunction task) {
 _actionQueue.add(task);
}

然后想到,队列是不是最好去重?或者可选去重。

那一个Function如何去重呢,我们可以使用它的hashCode,同一个FunctionhashCode相同

taskQueue.add(testFunction);
Future testFunction() async {
 await Future.delayed(const Duration(milliseconds: 1000));
 return Future.value(true);
}

即我们可以按上面这样定义,同一个类同一个实例下同一个Function,就是相同的hashCode。若是以下这种写法则hashCode不一样,每次add都相当于一个新的Function

taskQueue.add(() async {
 await Future.delayed(const Duration(milliseconds: 1000));
 return Future.value(true);
});

假如不需要去重,可以用第二种。也可以主动指定taskId来选择哪些需要去重(即使内容不一样),哪些不需要。

修改一下add,增加taskId。同时hashCode应该作为主要的key,修改一下队列类型。 最终如下:

/// 任务编号队列
List<String> _actionQueue = [];
/// 任务队列
Map<String, TaskEventFunction> _actionMap = {};
/// 指定taskId 为 -1 则不用去重
String add(TaskEventFunction task, {
 String? taskId,
}) {
 String? id = taskId;
 id ??= task.hashCode.toString();
 bool isContains = false;
 if (taskId != '-1') {
   isContains = _actionQueue.contains(id);
 } else {
   id = task.hashCode.toString();
 }
 if (!isContains) {
   _actionQueue.add(id);
   _actionMap[id] = task;
 }
 return id;
}

-1时则不去重,也把最终的taskId返回。

移除队列指定任务

有添加任务的方法,那也需有移除任务的方法

/// 这里需注意,add的时taskId为-1,那就直接把task传入,add时有返回,可以对应
bool remove(TaskEventFunction task, {String? taskId}) {
 String? id = taskId;
 id ??= task.hashCode.toString();
 if (_actionQueue.contains(id)) {
   _actionMap.remove(id);
   return _actionQueue.remove(id);
 }
 return false;
}

taskId/hashCode为准。

判断是否包含对应任务

使用者可以自己判断是否已包含对应的任务

/// 是否队列中包含该任务
/// [task] 与 [taskId] 应该只传一个
bool containers({TaskEventFunction? task, String? taskId}) {
 assert(task != null || taskId != null);
 String? id = taskId;
 id ??= task.hashCode.toString();
 return _actionQueue.contains(taskId);
}

执行队列任务

任务队列的进出基本成型,开始处理任务。很简单,取出任务,然后执行任务,最后移除任务

void startLoop() async {
 if (dealing || _actionQueue.isEmpty) {
   return;
 }
 dealing = true;
 String taskId = _actionQueue.first;
 TaskEventFunction? callback = _actionMap[taskId];
 if (callback == null) {
   _actionQueue.remove(taskId);
   return;
 }
try {
   await callback();
 } catch (e) {
   log('_actionQueue 出错 $e');
 } finally {
   _actionQueue.remove(taskId);
   _actionMap.remove(taskId);
   dealing = false;
   if (_actionQueue.isNotEmpty) {
     startLoop();
   }
 }
}

这里加了个dealing,表示任务正在处理中的状态,正在处理的话,不允许执行下一个任务。

在执行完成(或失败)后,自动触发下一个任务。add中也可以加入startLoop,使添加任务后自动启动任务循环。

任务条件

基本的任务队列已经完成。不过每个任务其实一般都会有个条件,确认符合当前场景才执行。比如说的确是新人且在首页,才显示新人优惠弹窗。

条件可以是整个任务队列统一的条件,也可以是某个任务特定的条件

typedef TaskConditionFunction = FutureOr<bool> Function();

这里类型用FutureOr<bool>,有可能需要等待一下才能确认条件。任务对应的条件,也根据taskId来存储。

/// 任务条件
Map<String, TaskConditionFunction> _actionCondition = {};
/// 总条件
TaskConditionFunction? _condition;

添加任务时加入条件

TaskEventFunction add(TaskEventFunction task, {
 String? taskId,
 TaskConditionFunction? condition,
}) {
   ...
if (condition != null && !_actionCondition.containsKey(id)) {
     _actionCondition[id] = condition;
   }
}

设置总条件,或补充条件

/// 设置允许执行条件
void setAcceptConditions(TaskConditionFunction condition,
   {String? taskId}) {
 if (taskId == null) {
   _condition = condition;
 } else {
   _actionCondition[taskId] = condition;
 }
}

执行任务前判断条件是否满足

bool canNext = await _nextConditions(taskId);
   if (canNext) {
     try {
       await callback();
     } catch (e) {
       log('_actionQueue 出错 $e');
     } finally {
       _actionQueue.remove(taskId);
       _actionMap.remove(taskId);
       dealing = false;
       if (_actionQueue.isNotEmpty) {
         startLoop();
       }
     }
   } else {
     // 不满足条件一般后续的也不会执行
     dealing = false;
   }
Future<bool> _nextConditions([String? id]) async {
 String taskId = id ?? _actionQueue.first;
 bool canNext = true;
 var taskCondition = _actionCondition[taskId];
 if (_condition != null) {
   canNext = await _condition!.call();
 }
 if (canNext && taskCondition != null) {
   canNext = await taskCondition();
 }
return Future.value(canNext);
}

原则上应该既满足总条件也满足任务条件

使用和总结

实例化TaskQueue

TaskQueue taskQueue = TaskQueue();

添加任务并开始任务

taskQueue.add(testFunction, condition: () async {
 await Future.delayed(const Duration(milliseconds: 200));
 return Future.value(true);
});
taskQueue.startLoop();
Future testFunction() async {
 return showDialog(...);
}

注意设置条件要返回bool

还可以加入类似延时等操作,跟条件一样配置即可。太久的操作不要像上面一样写在condition中,可能执行完之后又不满足了,根据具体情况考虑。

一般来说类似弹窗的returnawait showDialog就可以等待弹窗页面结束,再进行下一个。

跨页面还是当前页面的控制,只需要考虑是全局实例TaskQueue还是页面内实例TaskQueue

这样一个使用简单好用的任务队列就实现好了!

来源:https://juejin.cn/post/7108642127373926436

标签:Flutter,队列
0
投稿

猜你喜欢

  • jsp如何获取Session中的值

    2023-07-01 05:59:28
  • 基于C语言实现井字棋游戏

    2023-06-28 21:23:18
  • C++实现LeetCode(5.最长回文子串)

    2023-07-03 06:07:13
  • Flutter基于Dart Unwrapping Multiple Optional小技巧

    2023-07-05 11:44:19
  • android使用flutter的ListView实现滚动列表的示例代码

    2023-06-26 09:00:13
  • 输出的文本实现对齐的方法(超简单)

    2023-06-19 03:57:09
  • Android仿简书搜索框效果的示例代码

    2023-06-18 16:02:58
  • 解析Android 8.1平台SystemUI 导航栏加载流程

    2023-06-23 15:21:21
  • C++实现的链表类实例

    2023-07-04 08:36:36
  • 普通对象使用spring容器中的对象的实现方法

    2023-06-17 12:27:20
  • C#中使用split分割字符串的几种方法小结

    2023-06-18 17:23:21
  • Kotlin中常见内联扩展函数的使用方法教程

    2023-07-04 13:46:12
  • 彻底掌握C语言strcpy函数的用法

    2023-07-03 07:21:18
  • Spring整合Quartz实现定时任务调度的方法

    2023-07-07 00:55:55
  • JSON.toJSONString()空字段不忽略修改的问题

    2023-06-16 03:12:37
  • Android带进度条的文件上传示例(使用AsyncTask异步任务)

    2023-06-24 09:43:11
  • Android Camera+SurfaceView自动聚焦防止变形拉伸

    2023-06-18 06:35:54
  • c语言switch反汇编的实现

    2023-06-29 03:38:17
  • OpenCV实现人脸识别简单程序

    2023-07-07 00:31:12
  • flutter实现发送验证码功能

    2023-07-05 19:03:12
  • asp之家 软件编程 m.aspxhome.com