Flutter 假异步的实现示例

就像 android 有 handle 一样,消息队列这东西好像还真是系统必备,Flutter 也有自己的消息队列,只不过队列直接封装在了 Dart 的线程类型 Isolate 里面了,不过 Flutter 还是提供了 Futrue 这个 API 来专门来操作各种消息,以及实现基于消息队列的假异步

Flutter 的“异步”机制

这里的异步是加了引号的,可见此异步非真异步,而是假异步。Flutter 的 异步 不是开新线程,而是往所属线程的 消息队列 中添加任务,当然大家也可以按上文那样自己展开真异步操作

Flutter 对代码分2类: 同步代码和异步代码

  • 同步代码:传统一行行写下来,一行行执行的代码
  • 异步代码:通过 Future API 把任务添加到 Isolate 所属消息队列执行的伪异步
  • 执行顺序:先运行同步代码,再运行异步代码

为啥,很明显啊,异步代码是往消息队列里添加任务,那肯定得等现在的代码运行完了,线程有空闲了才能开始执行消息队列里的任务呀~

举个例子:

void test() {
  print("AA");
  Future(() => print("Futrue"));
  print("BB");
}
~~~~~~~~~~~log~~~~~~~~~~~~~
I/flutter (10064): AA
I/flutter (10064): BB
I/flutter (10064): Futrue

print("Futrue")) 任务等到最后才执行的...

Flutter 提供了往 消息队列 添加数据的 API: Future

往 MicroTask 队列添加任务

scheduleMicrotask((){
 // ...code goes here...
}); 

new Future.microtask((){
  // ...code goes here...
});

往 Event 队列添加任务

new Future(() {
 // ...code goes here...
});

Future 的基本使用

Future 对象是 Flutter 专门提供的,基于消息队列实现异步的类,Future 对象会把自身当做一个任务添加到消息队列中去排队执行

Future 对象接受的是一个函数,就是要执行的任务,用 () => ... 简写也是可以的

void task() {
  print("AA");
}

var futrue = Future(task);

创建 Future 任务方式:

  • Future()
  • Future.microtask()
  • Future.sync() - 同步任务
  • Future.value()
  • Future.delayed() - 延迟xx时间添加任务
  • Future.error() - 错误处理

我们来看几个代表性的:

Future.sync() - 阻塞任务,会阻塞当前代码,sync 的任务执行完了,代码才能走到下一行

void test() {
  print("AA");
  Future.sync(() => print("Futrue"));
  print("BB");
}
~~~~~~~~~~~~log~~~~~~~~~~~~~~
I/flutter (10573): AA
I/flutter (10573): Futrue
I/flutter (10573): BB

Future.delayed() - 延迟任务,指定xx时间后把任务添加到消息队列,要是消息队列前面有人执行的时间太长了,那么执行时间点就不能把握了,这点大家要知道

void test() {
  print("AA");
  Future.delayed(Duration(milliseconds: 500),() => print("Futrue"));
  print("BB");
}
~~~~~~~~~~~~log~~~~~~~~~~~~~~
I/flutter (10573): AA
I/flutter (10573): BB
I/flutter (10573): Futrue

Future 的链式调用

Future 也支持链式调用的,在 API 使用上也是很灵活的,提供了下面的选择给大家

.then - 在 Future 执行完后执行,相当于一个 callback,而不是重新创建了一个 Future

 Future.delayed(Duration(seconds: 1),(){
   print(("AAA"));
   return "AA";
  }).then((value){
   print(value);
  });

.catchError - future 不管在任何位置发生了错误,都会立即执行 catchError

  Future.delayed(Duration(seconds: 1),(){
   throw Exception("AAA");
  }).then((value){
   print(value);
  }).catchError((error){
   print(error);
  });

.whenComplete - 不管是否发生异常,在执行完成后,都会执行该方法

 Future.delayed(Duration(seconds: 1), () {
   throw Exception("AAA");
  }).then((value) {
   print(value);
  }).catchError((error) {
   print(error);
  }).whenComplete(() {
   print("complete...");
  });

.wait - 可以等待所有的 future 都执行完毕再走 then 的方法

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);
  });

大家想想啊

Futrue()
  .then()
  .then()
  ...

这样的链式写法不就是标准的去 callback 回调地狱的方式嘛

async/await 关键字

async/await 这组关键字是系统提供的另一种实现 异步 任务的 API, async/await 底层还是用 Futrue 实现的,从使用上看是对 Futrue 的简化,本质上还是基于 消息队列 实现的异步,是 假异步 ,和 Isoalte 是不一样的

async/await 的特点就是: 成对出现

  • async - 修饰方法,用 async 声明的方法都是耗时的
  • await - 调用 async 方法时使用,也可以在 async 方法内部是适用,await 表示阻塞,下面的任务必须等 await 调用的方法执行完之后才能执行

比如这样:

 anysncTest() async {
  print("async 休眠 start...");
  sleep(Duration(seconds: 1));
  print("async 休眠 end...");
 }

await anysncTest();

本质上 await 调用的方法其实是把这个方法包装到 Futrue 中去消息队列里执行,只不过是: Future.sync() 阻塞式的 Future 任务

async 在布局中也是可以直接用的

class TestWidgetState extends State<TestWidget> {
 int _count = 0;

 @override
 Widget build(BuildContext context) {
  return Material(
    FlatButton(
      onPressed: () async {
        _count = countEven(1000000000);
        setState(() {});
      },
      child: Text(
        _count.toString(),
      )),
  );
 }

async/await 是阻塞式的函数

实验1:

 // 这是异步任务代码
 aaa() async{
  print("main1...");
  await anysncTest();
  print("main2...");
  print("main3...");
 }

 anysncTest() async {
  print("async 休眠 start...");
  sleep(Duration(seconds: 1));
  print("async 休眠 end...");
 }

 // 点击按钮去执行
  Widget build(BuildContext context) {
  return RaisedButton(
   child: (Text("click!")),
   onPressed: () async {
    await aaa();
   },
  );
 }

可以看到 async/await

执行的方法的确是阻塞时的,至少在这个 async 方法里绝对是阻塞式的

实验2:

那么范围扩展一下,在 async 外面再来看看 async/await 是不是阻塞式的? 有人说 async/await 和协程一样 ,协程的关键点在于非竞争式资源,协程的概念中,当多个协程中有一个协程挂起之后,并不会阻塞 CPU,CPU 回去执行其他协程方法,直到有空闲了再来执行之前挂起后恢复的协程,虽然在协程看来我挂起了线程,但其实 CPU 不会被协程挂起阻塞,这点就是协程的核心优势,大大提升多线程下的执行效率。

从这点出发我们就能知道 async/await 是不是又一个协程了,看看他阻塞 CPU,我们在 await 之后看看 async 后面的代码会不会执行就 OK了

 // 还是这组方法
 aaa() async{
  print("main1...");
  await anysncTest();
  print("main2...");
  print("main3...");
 }

 anysncTest() async {
  print("async 休眠 start...");
  sleep(Duration(seconds: 1));
  print("async 休眠 end...");
 }

 // 执行,注意此时按钮的点击方法不是 async 的
 Widget build(BuildContext context) {
  return RaisedButton(
   child: (Text("click!")),
   onPressed: () {
    print("click1...");
    aaa();
    print("click2...");
    print("click3...");
   },
  );
 }
I/flutter ( 5733): click1...
I/flutter ( 5733): main1...
I/flutter ( 5733): async 休眠 start...
I/flutter ( 5733): async 休眠 end...
I/flutter ( 5733): click2...
I/flutter ( 5733): click3...
I/flutter ( 5733): main2...
I/flutter ( 5733): main3...

await 阻塞是真的阻塞 CPU 了,所以 async/await 不是协程,但是大家注意啊,在 await 结速阻塞之后执行的是 click2 也就是 async 外部的方法,说明 await 标记的方法返回的都是 Futrue 对象的说法是正确的,队列只有在线程空闲时才会执行,显然此时线程不是空闲的,点击方法还没执行完呢

实验3:

这次做对比实验,把点击事件也变成 async 的看看执行顺序

 // 还是这组方法
 aaa() async{
  print("main1...");
  await anysncTest();
  print("main2...");
  print("main3...");
 }

 anysncTest() async {
  print("async 休眠 start...");
  sleep(Duration(seconds: 1));
  print("async 休眠 end...");
 }

 // 执行
  Widget build(BuildContext context) {
  return RaisedButton(
   child: (Text("click!")),
   onPressed: () async {
    print("click1...");
    await aaa();
    print("click2...");
    print("click3...");
   },
  );
 }
I/flutter ( 5733): click1...
I/flutter ( 5733): main1...
I/flutter ( 5733): async 休眠 start...
I/flutter ( 5733): async 休眠 end...
I/flutter ( 5733): main2...
I/flutter ( 5733): main3...
I/flutter ( 5733): click2...
I/flutter ( 5733): click3...

这样看的话在 async 方法内部,是严格按照顺序执行的

async 方法的格式

1. async 标记的方法返回值都是 Futrue 类型的

上文书哦说 await 调用的方法返回的都是 Futrue 对象,那么就是说在声明 async 函数时,返回值都是 Futrue 类型的,Futrue 内部包裹实际的返回值类型

Futrue<String> getData() async {
 data = await http.get(Uri.encodeFull(url), headers: {"Accept": "application/json"});
}

Futrue<String> 我们可以不写,dart 也会自动推断出来,但是我们一定要知道是 Futrue 类型的,要不有时会报类型错误

我们在用的时候都是配合 await 使用的,这时候可以直接用具体类型值接返回值了

String data = await getData();

记住:

Future就是event,很多Flutter内置的组件比如前几篇用到的Http(http请求控件)的get函数、RefreshIndicator(下拉手势刷新控件)的onRefresh函数都是event。每一个被await标记的句柄也是一个event,每创建一个Future就会把这个Future扔进event queue中排队等候安检~

Stream

StreamFuture 一样都是假异步操作,区别是 Stream 可以接受多次数据,我不详细展开了,有待以后详细研究

Stream.fromFutures([
 // 1秒后返回结果
 Future.delayed(new Duration(seconds: 1), () {
  return "hello 1";
 }),
 // 抛出一个异常
 Future.delayed(new Duration(seconds: 2),(){
  throw AssertionError("Error");
 }),
 // 3秒后返回结果
 Future.delayed(new Duration(seconds: 3), () {
  return "hello 3";
 })
]).listen((data){
  print(data);
}, onError: (e){
  print(e.message);
},onDone: (){

});

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Flutter 网络请求框架封装详解

     Flutter 请求网络的三种方式 flutter 请求网络的方式有三种,分别是 Dart 原生的网络请求 HttpClient.第三方网络请求 http以及 Flutter 中的 Dio.我们可以比较一下这三种网络请求方式,然后封装为我们方便请求网络的工具类. Dart 原生的网络请求 HttpClient 实现 Dart 获取网络数据的请求,一般我们需要以下几个步骤: step 1: 原生的网络请求时不需要修改 pubspec.yaml 文件的,我们只需要在使用的地方引入所需包就可以了 i

  • Flutter质感设计之弹出菜单

    PopupMenuButton控件即弹出菜单控件,点击控件会出现菜单. import 'package:flutter/material.dart'; class MenusDemo extends StatefulWidget { @override _MenusDemoState createState() => new _MenusDemoState(); } class _MenusDemoState extends State<MenusDemo> { String _body

  • Flutter倒计时/计时器的实现代码

    在我们实现某些功能时,可能会有倒计时的需求. 比如发送短信验证码,发送成功后可能要求用户一段时间内不能再次发送,这时候我们就需要进行倒计时,时间到了才允许再次操作. 如下图: 为了实现这样场景的需求,我们需要使用 Timer.periodic . 一.引入Timer对应的库 import 'dart:async'; 二.定义计时变量 class _LoginPageState extends State<LoginPage> { ... Timer _timer; int _countdown

  • Flutter Android端启动白屏问题的解决

    问题描述 Flutter 应用在 Android 端上启动时会有一段很明显的白屏现象,白屏的时长由设备的性能决定,设备性能越差,白屏时间越长. 问题分析 其实启动白屏的问题在Android原生应用上也是一个常见问题,大致是因为从用户点击 Launcher Icon 到应用首页显示之间,Android 系统在完成应用的初始化工作,其流程如下: 在 Flutter Android 端上,白屏的问题会更加严重,因为除了 Android 应用启动耗时外,还增加了 Flutter 初始化耗时. 直到 Fl

  • 你必须掌握在Flutter中添加资源文件的方法

    在 Flutter 中,需要在根目录下的 pubspec.yaml 文件中配置资源的路径,资源才能被打包使用.现在,看看如何配置资源吧. 1. 添加图片资源文件 1.1 添加本地图片资源 flutter: assets: // 表示引入根目录下的 images 文件夹下的所有资源文件 - images/ // 只添加 images/ 下的 pci.png - images/pci.png 注意缩进!本地文件夹内的资源可以选择导入整个文件夹,或者只导入指定文件.使用:Image.asset("im

  • Flutter中ListView 的使用示例

    这个小例子使用的是豆瓣 API 中 正在上映的电影 的开放接口,要实现的主要效果如下: JSON 数据结构 Item 结构 Item 的结构是一个 Card 包含着一个 Row 然后这个 Row 里面左边是一个 Image ,右边是一个 Column 功能实现 material 库 Json 解析 网络请求 加载菊花 要实现上面四个功能,我们首先需要在 .dart 文件中引入如下代码 import 'dart:convert'; import 'package:http/http.dart' a

  • flutter实现仿boss直聘功能

    Flutter是Google使用Dart语言开发的移动应用开发框架,使用一套Dart代码就能构建高性能.高保真的iOS和Android应用程序,并且在排版.图标.滚动.点击等方面实现零差异. 2年前,RN刚出来时做了个仿拉钩的demo,react-native-lagou. 这次flutter来了,想感受一下,索性用目前flutter的版本写的一个仿boss直聘应用. 时间有限,没完全仿照,去掉了一些功能,但是界面风格一致,有参考价值. github地址:flutter仿boss直聘. 感悟 与

  • Flutter 假异步的实现示例

    就像 android 有 handle 一样,消息队列这东西好像还真是系统必备,Flutter 也有自己的消息队列,只不过队列直接封装在了 Dart 的线程类型 Isolate 里面了,不过 Flutter 还是提供了 Futrue 这个 API 来专门来操作各种消息,以及实现基于消息队列的假异步 Flutter 的"异步"机制 这里的异步是加了引号的,可见此异步非真异步,而是假异步.Flutter 的 异步 不是开新线程,而是往所属线程的 消息队列 中添加任务,当然大家也可以按上文那

  • 100 行代码实现Flutter自定义TabBar的示例代码

    Flutter 的确很强大,但美中不足的是生态还有待完善,没有出现像前端的 Antd 或 Element 那样全能的基础 UI 库. 由此带来的直接影响是开发效率提不上去,需要耗费大量的时间精力在基础组件的封装上. 官方的 TabBar 不满足需求,又没有合适的轮子,只好自己造轮子啦.接下来带你一步步实现自定义 TabBar-- 一.目标和效果 需求目标是: 这个页面不要 material 左侧统一的返回键和 Title 在右侧有取消按钮,点取消即返回 点击 Tab 可以实现 content 切

  • Flutter Drawer抽屉菜单示例详解

    本文实例为大家分享了Flutter Drawer抽屉菜单示例代码,供大家参考,具体内容如下 一.Flutter Drawer组件简介 1.源码查看 const Drawer({     Key? key,     this.elevation = 16.0, //阴影效果大小     this.child, //内容元素     this.semanticLabel, //关闭/打开抽屉时的通知信息   })  二.抽屉菜单示例 1.菜单项,使用 ListTile 实现 Expanded(  

  • Flutter 绘制风车实现示例详解

    目录 前言展示 1. 风车 1 的绘制 2. 风车 2 的绘制 3. 旋转动画的处理 4. 旋转动画的圈数 前言展示 最近源码看得比较多,本文来画点东西调节下心情,本绘制已收录于 FlutterUnit 的绘制集录,本文源码可参见[windmill.dart] .绘制内容非常简单,如下所示,两个样式的小风车:通过这两个小例子,可以学到: 路径的使用 画板的旋转变换 动画曲线与 Tween 的使用 风车1 风车2 1. 风车 1 的绘制 第一个风车非常简单,由四个 半圆 组成,每个部分直接的关系是

  • 封装flutter状态管理工具示例详解

    目录 引言 RxBinder 代码实现 Demo 完美运行 引言 关于 Flutter 状态管理,公司项目使用的是Bloc方案.Bloc 其实本质上是 provider 的封装扩展库,整体通过 InheritedWidget .Notifier 外加 Stream中转实现状态变更通知. 关于 Bloc 实现原理,有兴趣的同学可以观看这篇文章 Bloc原理解析 RxBinder 撇开Bloc内部实现策略,小轰尝试基于数据驱动模型,自定义一套状态管理工具.构思如下: 主要成员如下: RxBinder

  • flutter Bloc 实现原理示例解析

    目录 序言 1. 事件流 > 状态流 (中转) 2. 使用 BlocBuilder 实时监听状态变更, 如何实现的呢? 总结 扩展 序言 在flutter开发中,我们使用 bloc 框架,基于状态变更进行响应式开发.本篇文章,小轰将 bloc 核心业务块进行拆解简化,聊一聊它的实现思想,bloc 核心能力分为如下两点: 添加事件 event,将 '事件流' 转换为 '状态流' state 监听 bloc 流,每次 state 状态变更,通知 widget 更新 下面,用自定义Bloc的方式,来给

  • flutter text组件使用示例详解

    目录 正文 Text组件 Text组件构造器上的主要属性 正文 flutter组件的实现参考了react的设计理念,界面上所有的内容都是由组件构成,同时也有状态组件和无状态组件之分,这里简单介绍最基本的组件. 在组件代码的书写方式上,web端开发的样式主要有由css进行控制,而客户端开发根据使用的技术栈不同,写法也稍微有些不同:ReactNative的写法和web比较类似,但是ReactNative是使用StyleSheet.create()方法创建样式对象,以内联的方式进行书写. import

  • Flutter CustomPaint自定义绘画示例详解

    目录 正文 CustomPaint 介绍 绘制点 PointMode3种模式 绘制线 和路径 绘制五子棋 总结 正文 CustomPaint是Flutter中用于自由绘制的一个widget,它与android原生的绘制规则基本一致,以当前Canves(画布)的左上角为原点进行绘制.在有些场景中,我们会需要绘制一些高度定制化的组件,比如 UI 设计师给我们出了个难题 —— 弄一个奇形怪状的边框.这个时候我们就不能直接使用 Flutter 自带的那些组件了,而是需要手动绘制组件,那就会需要用到 Cu

  • mock.js实现模拟生成假数据功能示例

    本文实例讲述了mock.js实现模拟生成假数据功能.分享给大家供大家参考,具体如下: mock使用方法很简单, 下面是简单的用法, 详细的用法可以看官方文档, 写的很清楚, 下面的代码直接拷贝到本地html文件, 双击打开即可生成你想要的数据 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible"

  • Flutter状态管理Provider示例解析

    目录 什么是状态管理 常见的状态管理框架有哪些 Provider Redux GetX Provider 使用 添加依赖 导入应用 定义需要共享的数据 在应用程序入口初始化 使用共享数据 状态管理的好处 结束语 什么是状态管理 状态管理是一个十分广泛的概念,因为状态无处不在.从广义角度讲状态管理就是页面跟代码.跟服务器进行数据同步.例如:你在某购物应用内,添加了一件商品,你需要在购物车页面进行展示.这就是一种状态.相反的,你在商品页面点击了添加到购物车,却在购物车页面没有发现或者过段时间它才展示

随机推荐