Flutter 异步编程原理(上)

简介: Flutter 异步编程原理(上)

Dart 中的事件循环模型


下面是 event loop 大致的运行图:

消息队列采用先进先出,下面引用了几张 Dart 开发者网站的介绍:


image.png


将消息换成具体的类型后,类似下图:


image.png


这个很好理解,事件 events 加到 Event queue 里,Event loop 循环从 Event queue 里取 Event 执行。

Dart 是一种单线程模型运行语言,其运行原理如下图所示:


image.png


Dart 在单线程中是以消息循环机制来运行的,其中包含两个任务队列,一个是“微任务队列” microtask queue,另一个叫做“事件队列” event queue。 从图中可以发现,微任务队列的执行优先级高于事件队列。


现在我们来介绍一下Dart线程运行过程,如上图中所示,入口函数 main() 执行完后,消息循环机制便启动了。 首先会按照先进先出的顺序逐个执行微任务队列中的任务,当所有微任务队列执行完后便开始执行事件队列中的任务,事件任务执行完毕后再去执行微任务, 如此循环往复,生生不息。


在Dart中,所有的外部事件任务都在事件队列中,如IO、计时器、点击、以及绘制事件等,而微任务通常来源于Dart内部,并且微任务非常少, 之所以如此,是因为微任务队列优先级高,如果微任务太多,执行时间总和就越久,事件队列任务的延迟也就越久, 对于GUI应用来说最直观的表现就是比较卡,所以必须得保证微任务队列不会太长。 在事件循环中,当某个任务发生异常并没有被捕获时,程序并不会退出,而直接导致的结果是当前任务的后续代码就不会被执行了, 也就是说一个任务中的异常是不会影响其它任务执行的。


我们可以看出,将任务加入到MicroTask中可以被尽快执行,但也需要注意,当事件循环在处理MicroTask队列时,Event队列会被卡住,应用程序无法处理鼠标单击、I/O消息等等事件。 同时,当事件循坏出现异常时,dart 中也可以通过 try/catch/finally 来捕获异常。


任务调度


MicroTask 任务队列

添加到 MicroTask 任务队列两种方法:

import 'dart:async';
void myTask(){
  print("this is my task");
}
void main() {
  /// 1. 使用 scheduleMicrotask 方法添加
  scheduleMicrotask(myTask);
  /// 2. 使用Future对象添加
  Future.microtask(myTask);
}

两个 task 都会被执行,控制台输出:

this is my task
this is my task


Event 任务队列

import 'dart:async';
void myTask(){
  print("this is my event task");
}
void main() {
  Future(myTask);
}


控制台输出:

this is my event task


示例:

import 'dart:async';
void main() {
  print("main start");
  Future((){
    print("this is my task");
  });
  Future.microtask((){
    print("this is microtask");
  });
  print("main stop");
}


控制台输出:


main start
main stop
this is microtask
this is my task

可以看到,代码的运行顺序并不是按照我们的编写顺序来的,将任务添加到队列并不等于立刻执行,它们是异步执行的,当前main方法中的代码执行完之后,才会去执行队列中的任务,且MicroTask队列运行在Event队列之前。


延时任务

Future.delayed(new  Duration(seconds:1),(){
    print('task delayed');
});

使用 Future.delayed 可以使用延时任务,但是延时任务不一定准

import 'dart:async';
import 'dart:io';
void main() {
  var now = DateTime.now();
  print("程序运行开始时间:" + now.toString());
  Future.delayed(Duration(seconds:1),(){
    now = DateTime.now();
    print('延时任务执行时间:' + now.toString());
  });
  Future((){
    // 模拟耗时5秒
    sleep(Duration(seconds:5));
    now = DateTime.now();
    print("5s 延时任务执行时间:" + now.toString());
  });
  now = DateTime.now();
  print("程序结束执行时间:" + now.toString());
}


输出结果:


程序运行开始时间:2019-09-11 20:46:18.321738
程序结束执行时间:2019-09-11 20:46:18.329178
5s 延时任务执行时间:2019-09-11 20:46:23.330951
延时任务执行时间:2019-09-11 20:46:23.345323
复制代码

可以看到,5s 的延时任务,确实实在当前程序运行之后的 5s 后得到了执行,但是另一个延时任务,是在当前延时的基础上又延时执行的, 也就是,延时任务必须等前面的耗时任务执行完,才得到执行,这将导致延时任务延时并不准。


Future 与 FutureBuilder


Future 的所有API的返回值仍然是一个Future对象,所以可以很方便的进行链式调用。


Future 使用

Future 的几种创建方法:

  • Future() 默认异步方法
  • Future.microtask() 微任务队列
  • Future.sync() 同步方法
  • Future.value() 返回指定 value 值
  • Future.delayed() 延时
  • Future.error() 错误


then 回调、 catchError 捕获异常和 whenComplete 一定执行


import 'dart:async';
void main() {
  print("main start");
  Future.delayed(new Duration(seconds: 2),(){
    //return "hi world!";
    throw AssertionError("Error");
  }).then((data){
    //执行成功会走到这里  
    print("success");
  }).catchError((e){
    //执行失败会走到这里
    print(e);
  }).whenComplete((){
    //无论成功或失败都会走到这里
    print("this is the end...");
  });
  print("main stop");
}

输出:


main start
main stop
Assertion failed


在本示例中,我们在异步任务中抛出了一个异常,then 的回调函数将不会被执行,取而代之的是 catchError 回调函数将被调用;但是,并不是只有 catchError 回调才能捕获错误,then 方法还有一个可选参数 onError,我们也可以它来捕获异常:


import 'dart:async';
void main() {
  print("main start2");
  Future.delayed(new Duration(seconds: 2), () {
    //return "hi world!";
    throw AssertionError("Error");
  }).then((data) {
    print("success");
  }, onError: (e) {
    print(e);
  });
  print("main stop2");
}


结果:

main start2
main stop2
Assertion failed


wait 完成后回调Future.wait 表示多个任务都完成之后的回调。


import 'dart:async';
void main() {
  print("main start4");
  Future.wait([
    // 2秒后返回结果
    Future.delayed(new Duration(seconds: 2), () {
      return "hello";
    }),
    // 4秒后返回结果
    Future.delayed(new Duration(seconds: 4), () {
      return " world";
    })
  ]).then((results){
    print(results[0]+results[1]);
  }).catchError((e){
    print(e);
  });
  print("main stop4");
}


结果:

main start4
main stop4
hello world




目录
相关文章
|
15天前
|
开发者 容器
Flutter&鸿蒙next 布局架构原理详解
本文详细介绍了 Flutter 中的主要布局方式,包括 Row、Column、Stack、Container、ListView 和 GridView 等布局组件的架构原理及使用场景。通过了解这些布局 Widget 的基本概念、关键属性和布局原理,开发者可以更高效地构建复杂的用户界面。此外,文章还提供了布局优化技巧,帮助提升应用性能。
78 4
|
15天前
|
存储 Dart 前端开发
flutter鸿蒙版本mvvm架构思想原理
在Flutter中实现MVVM架构,旨在将UI与业务逻辑分离,提升代码可维护性和可读性。本文介绍了MVVM的整体架构,包括Model、View和ViewModel的职责,以及各文件的详细实现。通过`main.dart`、`CounterViewModel.dart`、`MyHomePage.dart`和`Model.dart`的具体代码,展示了如何使用Provider进行状态管理,实现数据绑定和响应式设计。MVVM架构的分离关注点、数据绑定和可维护性特点,使得开发更加高效和整洁。
145 3
|
21天前
动画控制器在 Flutter 中的工作原理
【10月更文挑战第18天】总的来说,动画控制器 `AnimationController` 在 Flutter 中起着关键的作用,它通过控制动画的数值、速度、节奏和状态,实现了丰富多彩的动画效果。理解它的工作原理对于我们在 Flutter 中创建各种精彩的动画是非常重要的。
|
28天前
|
容器
Flutter&鸿蒙next 布局架构原理详解
Flutter&鸿蒙next 布局架构原理详解
|
5月前
|
存储 开发框架 JavaScript
深入探讨Flutter中动态UI构建的原理、方法以及数据驱动视图的实现技巧
【6月更文挑战第11天】Flutter是高效的跨平台移动开发框架,以其热重载、高性能渲染和丰富组件库著称。本文探讨了Flutter中动态UI构建原理与数据驱动视图的实现。动态UI基于Widget树模型,状态变化触发UI更新。状态管理是关键,Flutter提供StatefulWidget、Provider、Redux等方式。使用ListView等可滚动组件和StreamBuilder等流式组件实现数据驱动视图的自动更新。响应式布局确保UI在不同设备上的适应性。Flutter为开发者构建动态、用户友好的界面提供了强大支持。
98 2
|
1月前
|
机器学习/深度学习 开发框架 Dart
Flutter asynchronous 异步编程技巧
本文深入探讨了Flutter中的异步编程技巧,包括Future、Microtask及并发处理的最佳实践。文章详细讲解了Future.wait、FutureBuilder和Microtask的应用,帮助开发者提升应用性能。通过实例演示了如何利用Future.wait实现并发执行,FutureBuilder简化UI构建,以及Microtask的高优先级执行特性。适合希望优化Flutter应用异步性能的开发者阅读。
|
3月前
|
Dart 前端开发 JavaScript
Flutter&Dart-异步编程Future、Stream极速入门
Flutter&Dart-异步编程Future、Stream极速入门
72 4
Flutter&Dart-异步编程Future、Stream极速入门
|
4月前
|
Dart JavaScript Java
flutter 架构、渲染原理、家族
flutter 架构、渲染原理、家族
85 3
|
4月前
|
Dart
flutter 之 Dart 异步编程【详解】
flutter 之 Dart 异步编程【详解】
44 0
|
6月前
|
Android开发
Flutter完整开发实战详解(六、 深入Widget原理),2024百度Android岗面试真题收录解析
Flutter完整开发实战详解(六、 深入Widget原理),2024百度Android岗面试真题收录解析