温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Flutter队列任务如何实现

发布时间:2022-06-14 13:50:02 来源:亿速云 阅读:225 作者:iii 栏目:开发技术

本篇内容介绍了“Flutter队列任务如何实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

    队列

    任务队列,那当然要有个队列。这个队列的任务内容应该是返回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

    “Flutter队列任务如何实现”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!

    向AI问一下细节

    免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

    AI