Flutter| 事件循环,Fluture(上)

简介: Flutter| 事件循环,Fluture(上)

在 Dart 中,没有多线程的概念,所谓的异步操作全部都是在一个线程里面执行的, 并且不会造成卡顿的原因就是事件循环(Event Loop),


如下图所示,在程序的运行过程中,会有两个事件


image.png


补充上图:Micortask Queue 为空 才会执行 EventQueue ,EventQueue 为空时程序结束,实际上,事件循环从启动的之后会一直执行。


在程序执行过程中,如果有异步操作,这个操作就会添加到队列中,当发现队列不为空时,就会然后不断的从队列中取出事件在执行


Microtask Queue


一个顶级的队列,只要这个队列里面不是空的,就一定会执行该队列中的任务,


scheduleMicrotask(() {
  print("Hello Flutter");
});


Future.microtask() //内部调用的也是上面的函数


但是需要注意的是,一般的实战中,我们不会手动给这个队列里面添加事件,该队列一般都是由 Dart 自己来处理的。


Event Queue


普通的事件队列,比 Microtask Queue 低了一个等级,在 Microtask Queue 中没有任务的时候才会执行该队列中的任务


需要异步操作的代码都会放在 EventQueue 中执行,例如:


Future((){
  print('这里会被放在 EventQueu 中执行');
})
Future.delayed(Duration(seconds: 1), () {
  print('这里会被放在 EventQueu 中执行');
});


直接执行的代码


Future.sync(() => print('Hello'));
Future.value(() => print('world'));
xxx.then()


Future


Flutter 相当于是一个盒子,内部的代码最终会交给 EventQueue 来执行,Future 的状态大致可分为三种,如下:


Future(() {
    print('未完成状态');
})
.then((value) => print('已完成状态'))
.catchError((value) => print('异常状态'));


我们程序中的大部分异步操作都是围绕着这三种状态进行的。


Future 常用的函数


Future.error()
Future(() {
  return Future.error(Exception());
}).then((value) => print('已完成状态')).catchError((value) => print('异常状态'));


创建一个以异常结束的 Future,上面代码最终会执行到 catchError 中。


Future.whenComplete()


类似于 try catch 后面的 finnaly,无论成功和失败,最终都会执行到这里


Future.them 链式调用


//在 them 中可以接继续返回值,该值会在下一个链式的 then 调用中拿到返回的结果
getNetData().then((value) {
  //支持成功到此处
  print(value);
  return "data1";
}).then((value) {
  print(value);
  return "data2";
}).then((value) {
  print(value);
}).catchError((error) {
  //执行失败到此处
  print(error);
}).whenComplete(() => print("完成"));


Future.wait()


如果要等到多个异步任务都结束之后再进行一些操作,可以使用 Future.wait


Future.wait([getNetData(), getNetData(), getNetData()]).then((value) {
  value.forEach((element) {
    print(element);
  });
});


Future.delayed()


延时指定的时间后在执行


Future.delayed(Duration(seconds: 3), () {
    return "3秒后的信息";
  }).then((value) {
    print(value);
  });


async,await


async:用来表示函数是异步的,定义的函数会返回一个 Future 对象,可以使用 then 添加回调函数


await :后面是一个 Future,表示等待改异步任务的完成,异步完成之后才会继续往下走,await 必须出现在 async 的内部


void main() {
  print("start ----------->");
  getNetData().then((value) => print(value));
  print("end -------------->");
}
// async 会将返回的结果封装为 Future 类型
getNetData() async {
  var result1 = await Future.delayed(Duration(seconds: 3), () {
    return "网络数据1";
  });
  var result2 = await Future.delayed(Duration(seconds: 3), () {
    return "网络数据2";
  });
  return result1 + "-----" + result2;
}


FutureBuilder


监听一个 Future,以 Future 的状态来进行 setState


class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: Future.value(10),
        builder: (BuildContext context, AsyncSnapshot<dynamic> snap) {
          return DemoWidget()
        });
  }
}


构造


future :接受一个 future,当 future 的值发生变化之后,就会自动调用下面的 build 函数,


initialData:初始值,在 future 没完成的时候可以暂时使用该值,该值会放在 AsyncSnapshot 的 data 中,在 future 未完成的时候可以使用该值。在 future 出错的时候,该值会被 AsyncSnapshot 从 data 中删掉


builder:返回一个 Widget


AsyncSnapshot 用来保存 future 最近的状态,这个状态只有两个情况,一种是有数据 data,一种是错误状态 error。 AsyncSnapshot 中还有 ConnectionState 状态,分别表示的是 none :没有传递 future,waiting:等待中,active:TODO ,done :表示已经完成


FutureBuilder 的作用就是根据 future 的状态来判断当前页面需要显示哪些 widiget,例如 future 在等待的时候显示加载框,完成之后显示内容等。


需要注意的一点是当状态为 done 是,可能会有两种情况,一种是 future 成功了,另一种是 future 失败了,内部有异常,这个时候就不应该获取 data,而是判断 snap.hasData 来进行判断。


示例


class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: Future.value(10),
        builder: (BuildContext context, AsyncSnapshot<dynamic> snap) {
          if (snap.connectionState == ConnectionState.waiting) {
            return CircularProgressIndicator();
          }
          if (snap.connectionState == ConnectionState.done) {
            if (snap.hasData) {
              return DemoWidget();
            } else {
              return Text(snap.error);
            }
          }
          throw "should not happen";
        });
  }
}
//在精简一些
class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: Future.value(10),
        builder: (BuildContext context, AsyncSnapshot<dynamic> snap) {
          //如果有数据  
          if (snap.hasData) {
            return DemoWidget();
          }
           //如果发生错误
          if (snap.hasError) {
            return Text(snap.error);
          }
          // 等待中,显示加载框
          return CircularProgressIndicator();
        });
  }
}


源码浅析


/// State for [FutureBuilder].
class _FutureBuilderState<T> extends State<FutureBuilder<T>> {
  //....
  @override
  void initState() {
      //如果没有初始值,则先设置Wie none 状态,如果有,则传入初始值 
        _snapshot = widget.initialData == null
        ? AsyncSnapshot<T>.nothing()
        : AsyncSnapshot<T>.withData(ConnectionState.none, widget.initialData as T);
    _subscribe();
  }
  @override
  void didUpdateWidget(FutureBuilder<T> oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.future != widget.future) {
     //....
      _subscribe();
    }
  }
  void _subscribe() {
    if (widget.future != null) {
      final Object callbackIdentity = Object();
      _activeCallbackIdentity = callbackIdentity;
      widget.future!.then<void>((T data) {
        if (_activeCallbackIdentity == callbackIdentity) {
          //任务执行完成,将数据传给 _snapshot ,并刷新
          setState(() {
            _snapshot = AsyncSnapshot<T>.withData(ConnectionState.done, data);
          });
        }
      }, onError: (Object error, StackTrace stackTrace) {
        if (_activeCallbackIdentity == callbackIdentity) {
          setState(() {
              //出现错误
            _snapshot = AsyncSnapshot<T>.withError(ConnectionState.done, error, stackTrace);
          });
        }
        assert(() {
           //判断调试状态 
          if(FutureBuilder.debugRethrowError) {
            Future<Object>.error(error, stackTrace);
          }
          return true;
        }());
      });
       //没有完成则是等待状态
      _snapshot = _snapshot.inState(ConnectionState.waiting);
    }
  }


源码其实很简单,仔细看一下就知道整个流程了


StreamBuilder


介绍


上面的 FutureBuilder 只能给我们一个值,而 StreamBuildder 可以给我们一连串的值 ,例如:


final stream = Stream.periodic(Duration(seconds: 1), (count) => count++);
stream.listen((event) {
  print(event);
});


示例


class _MyHomePageState extends State<MyHomePage> {
  final stream = Stream.periodic(Duration(seconds: 1), (count) => count++);
  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      color: Colors.white,
      child: DefaultTextStyle(
        style: TextStyle(fontSize: 30, color: Colors.black),
        child: StreamBuilder(
            stream: stream,
            builder: (BuildContext context, AsyncSnapshot<dynamic> snap) {
              switch (snap.connectionState) {
                case ConnectionState.none:
                  return Text("NONE:没有数据流");
                  break;
                case ConnectionState.waiting:
                  return Text("WAITING:等待数据流");
                  break;
                case ConnectionState.active:
                  if (snap.hasData) {
                    return Text(snap.data.toString());
                  }
                  if (snap.hasError) {
                    return Text(snap.error.toString());
                  }
                  break;
                case ConnectionState.done:
                  return Text("DONE:数据流已关闭");
                  break;
              }
              return CircularProgressIndicator();
            }),
      ),
    );
  }
}


其实和 FutureBuilder 差不多,只不过多了一个 active 状态,这个状态在上面没有说是因为用不到**,在这里的意思指的就是数据流是否为活跃的**,如果是活跃的,则就可以获取他的值了


创建方式及常用的函数


使用 Stream.periodic 的方式来创建一个数据流,如上面的示例所示


读取文件的方式


File("").openRead().listen((event) { 
})


将读取的文件信息以数据流的方式转给我们


使用 StreamController


final controller = StreamController();
controller.stream.listen((event) {
  print('$event');
});
controller.sink.add(12);
controller.sink.add(13);
controller.sink.add(14);
controller.sink.add(15);
controller.sink.addError("Error");
//关闭后则不能进行任何添加操作
controller.close();


相关文章
|
移动开发 Dart 前端开发
从架构到源码:一文了解Flutter渲染机制
Flutter从本质上来讲还是一个UI框架,它解决的是一套代码在多端渲染的问题。在渲染管线的设计上更加精简,加上自建渲染引擎,相比ReactNative、Weex以及WebView等方案,具有更好的性能体验。本文将从架构和源码的角度详细分析Flutter渲染机制的设计与实现。较长,同学们可收藏后再看。
7996 1
从架构到源码:一文了解Flutter渲染机制
|
Java Shell Linux
Android——编译(一):编译make的基础知识
Android——编译(一):编译make的基础知识
331 0
|
编解码 测试技术 Android开发
Android经典实战之用 CameraX 库实现高质量的照片和视频拍摄功能
本文详细介绍了如何利用CameraX库实现高质量的照片及视频拍摄功能,包括添加依赖、初始化、权限请求、配置预览与捕获等关键步骤。此外,还特别针对不同分辨率和帧率的视频拍摄提供了性能优化策略,确保应用既高效又稳定。
1298 1
Android经典实战之用 CameraX 库实现高质量的照片和视频拍摄功能
|
JavaScript
vscode——如何调试typescript
vscode——如何调试typescript
234 4
|
存储 JSON 数据格式
解决使用flutter_intl出现警告No @@locale or _locale field found in intl_en
解决使用flutter_intl出现警告No @@locale or _locale field found in intl_en
256 2
|
存储 Dart 开发工具
flutter实现语言的国际化
【7月更文挑战第16天】
330 1
Flutter-无限循环滚动标签
Flutter-无限循环滚动标签
227 0
|
容器
Flutter Container设置 width 无效
Flutter Container设置 width 无效
Flutter 滚动距离来设置TabBar的位置,点击TabBar滚动的到指定的位置
Flutter 滚动距离来设置TabBar的位置,点击TabBar滚动的到指定的位置
|
前端开发 JavaScript
vue3实现验证码校验的功能
最近想实现使用vue3实现一个简易的前端验证码校验的功能,就花了点时间实现了,这只是一个简易版的,但是用在项目中是没有啥问题的,废话不多说,先来看下最终实现的效果。写着玩的 哈哈
698 0