本篇内容介绍了“Flutter队列任务如何实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
任务队列,那当然要有个队列。这个队列的任务内容应该是返回Future
的Function
,因为我需要得到他处理完成的结果,比如等待弹窗关闭时return
,才表示这个任务被完成。
typedef TaskEventFunction = Future Function(); class TaskQueue { List<TaskEventFunction> _actionQueue = []; }
然后是加入队列的方法add
void add(TaskEventFunction task) { _actionQueue.add(task); }
然后想到,队列是不是最好去重?或者可选去重。
那一个Function
如何去重呢,我们可以使用它的hashCode,同一个Function
的hashCode相同。
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
中,可能执行完之后又不满足了,根据具体情况考虑。
一般来说类似弹窗的return
或await showDialog
就可以等待弹窗页面结束,再进行下一个。
跨页面还是当前页面的控制,只需要考虑是全局实例TaskQueue
还是页面内实例TaskQueue
。
“Flutter队列任务如何实现”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。