網站首頁 編程語言 正文
前言
在電商的應用中,最常見的就是在首頁或完成某事件之后,彈出一堆的活動/廣告。假如重疊彈出,很丑,給用戶的體驗也不好,所以一般都會依次依條件的彈出。
下面講講我是怎么實現一個方便的隊列任務管理。
隊列
任務隊列,那當然要有個隊列。這個隊列的任務內容應該是返回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
。
原文鏈接:https://juejin.cn/post/7108642127373926436
相關推薦
- 2022-12-08 C語言中-a++和-++a運算順序實例解析_C 語言
- 2023-08-13 微信小程序底部導航欄最多只能顯示五個,解決辦法
- 2022-04-05 關于redis客戶端連接不上
- 2022-07-16 ssh遠程連接docker
- 2022-07-06 React創建對話框組件的方法實例_React
- 2022-03-22 C++名稱空間特性_C 語言
- 2023-05-12 Python時間戳轉換為字符串與字符串轉換為時間戳_python
- 2022-08-01 Flask框架之數據交互的實現_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支