<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在電商的應用中,最常見的就是在首頁或完成某事件之後,彈出一堆的活動/廣告。假如重疊彈出,很醜,給使用者的體驗也不好,所以一般都會依次依條件的彈出。
下面講講我是怎麼實現一個方便的佇列任務管理。
任務佇列,那當然要有個佇列。這個佇列的任務內容應該是返回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 佇列任務內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45