FlutterFlutterDart

Dart 异步(六)

2020-02-28  本文已影响0人  向日花开

同步与异步

模拟一个耗时操作,来看一下下面例子:

同步

在这个例子中,我们用 doSomeThingNormal 模拟一个耗时操作,该操作需要两秒后才能返回数据。

  doSomeThingNormal() {
    sleep(Duration(seconds: 2));
    return "Normal---->doSomeThing";
  }
  main() {
    print("main---->start");
    print(doSomeThingNormal());
    print("main---->end");
  }

运行

doSomeThingNormal 会阻塞执行两秒后,才会继续执行,这会导致卡顿,显然不是我们想要的效果。

flutter: main---->start
//停顿两秒
flutter: Normal---->doSomeThing
flutter: main---->end

异步

我们再来看看异步,我们使用关键字 async,使用 async 关键字的方法会返回一个 Future 对象,我们做如下改动:

  doSomeThingAsync() async {
    return Future<String>(() {
      sleep(Duration(seconds: 2));
      return "Async---->doSomeThing";
    });
  }

  main1() {
    print("main---->start");
    print(doSomeThingAsync());
    print("main---->end");
  }

运行

flutter: main---->start
flutter: Instance of 'Future<dynamic>'
flutter: main---->end

发现程序丝滑般顺畅。So,

Dart 是一门单线程语言,在遇到延迟的运算(网络请求,IO 操作)时,线程按顺序执行时就会阻塞,导致用户觉得界面卡顿,这显然是不被允许的,那如何处理这些耗时操作呢?

什么是事件循环

Dart 的事件循环如下图,循环中有两个队列,一个是微任务队列 (MicroTask queue), 一个是事件队列(Event queue)。

如图可知事件的循环过程如下:

总之,微任务队列会一次性全部处理,事件队列则每次只取一个处理。
了解这个流程,我们才能理解 Dart 代码的执行顺序。

如何把放在微任务队列

Dart 中提供 scheduleMicrotask 方法,让代码以微任务的方式执行。

 scheduleMicrotask((){
      print("A Microtask");
    });

如何把代码放到事件队列

事件队列包含外部事件,例如 I/O, Timer,点击,网络事件,异步方法等

async_event() async{
    print("normal_event");
  }

那么如下代码的运行顺序你清楚吗,”normal_event“会被首先打印吗?

  main2() {
    //事件队列
    async_event();
    //微任务队列
    foo();
  }

  foo() {
    print("foo");
    scheduleMicrotask(foo);
  }

  async_event() async {
    print("normal_event");
  }

答案是不会打印”normal_event“,因为微任务队列中一直有任务,事件队列没有机会运行。

Dart 实现异步的方式

async/await

上面的例子中我们已经见到过 async 关键字,那 await 怎么用?细心的你可能已经发现上面:

 main1() {
    print("main---->start");
    print(doSomeThingAsync());
    print("main---->end");
  }

doSomeThingAsync()打印出来的结果是:

flutter: Instance of 'Future<dynamic>'

是一个 Future 对象,但我们想要的其实是字符串对象。如何获取字符串对象?我们可以用 await 接收它,做如下改动:

  doThings() async {
    String msg = await doSomeThingAsync();
    print(msg);
  }

  main4() {
    print("main---->start");
    doThings();
    print("main---->end");
  }

可见我们又抽出一个异步方法 doThings,因为 规定 await 必须在 async 修饰的方法中使用 ,因此 doThings 方法必须用 async 修饰。

运行
flutter: main---->start
flutter: main---->end
//等待两秒
flutter: Async---->doSomeThing

我把这个小案例的代码抽一下,看看它的执行顺序:

  fun1() async {
    print("fun1---->start");
    String msg = await fun2();
    print("fun1:msg:\t${msg}");
  }

  fun2() async {
    print("fun2---->");
    return "Hello world";
  }

  main5() {
    print("main---->start");
    fun1();
    print("main---->end");
  }
输出
flutter: main---->start
flutter: fun1---->start
flutter: fun2---->
flutter: main---->end
flutter: fun1:msg:  Hello world

async/await 的好处就是让异步的写法上类似于同步方法,同样的它有不好的一面,就是这样很容易陷入”回调地域“,由 fun1 fun2 的例子可见一斑,如果 fun2 的输出还依赖其他异步函数,那程序可读性将大打折扣,一般异步操作都伴随着 try catch ,程序在包一层 try catch 那 async/await 这种写法就不那么友好了,为了避免这样的问题,JS 引入了 Promise。而 Dart, 引入了 Future。

Future

这里简单的介绍一下 Future 的使用。

 Future(() => print("立刻在Event queue中运行的Future"));
Future.delayed(
        Duration(seconds: 2), () => print("2秒后在Event queue中运行的Future"));
Future.microtask(() => print('在Microtask queue里运行的Future'));
Future.sync(() => print('同步运行的Future'));
代码
  main6() {
    Future(() => print("立刻在Event queue中运行的Future"));
    Future.delayed(
        Duration(seconds: 2), () => print("2秒后在Event queue中运行的Future"));
    Future.microtask(() => print('在Microtask queue里运行的Future'));
    Future.sync(() => print('同步运行的Future'));
  }
运行
flutter: 同步运行的Future
flutter: 在Microtask queue里运行的Future
flutter: 立刻在Event queue中运行的Future
flutter: 2秒后在Event queue中运行的Future

由执行结果的先后顺序能使事件机制更深刻一点。

链式调用

通过调用 then 来把回调函数串起来,这样就解决了"回调地狱"的问题。

Future(()=> print('task'))
    .then((_)=> print('callback1'))
    .then((_)=> print('callback2'));
异常处理

  //链式调用,捕获异常
   Future(() => print("立刻在Event queue中运行的Future")).then(fun1(), onError: (e) {
      handleError(e);
    }).then(fun2(), onError: (e) {
      handleError(e);
    });

   Future(()=> throw 'we have a problem')
      .then((_)=> print('callback1'))
      .then((_)=> print('callback2'))
      .catchError((error)=>print('$error'));


Future(()=> throw 'we have a problem')
    .then((_)=> print('callback1'))
    .then((_)=> print('callback2'))
    .catchError((error)=>print('$error'))
    .whenComplete(()=> print('whenComplete'));

无论这个 Future 是正常执行完毕还是抛出异常,whenComplete 都一定会被执行。

Future 的两种状态

Future 在执行的整个过程中,我们通常把它划分成了两种状态:

未完成状态(uncompleted)
完成状态(completed)

以上的案例我们发现我们并不能手动控制 Future 的完成状态,都是靠系统调度完成之后置 Future 的状态为完成。除此之外,我们还可以用 Completer 稍微控制一下。

  main8() {
    // 实例化一个Completer
    var completer = Completer();
    // 这里可以拿到这个completer内部的Future
    var future = completer.future;
    future.then((_)=>print("then"));

    //...someThing elses
    completer.complete("finished");
  }

但这个没发现有多大用,Dart 的异步我其实只说了一些很浅显的点,背后还有很多东西,如 Isolate,Flutter 的引擎线程模式。这以后有机会我在研究一下,学海无涯啊。

最后

贴一张自己学习Flutter的公众号,感兴趣的小伙伴可以一起学习哦。。。

全部代码

import 'dart:async';
import 'dart:ffi';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_travel/pages/dart_pages/dart_oop_page.dart';

class DartAsyncPage extends StatefulWidget {
  @override
  _DartAsyncPageState createState() => _DartAsyncPageState();
}

class _DartAsyncPageState extends State<DartAsyncPage> {
  @override
  void initState() {
    // TODO: implement initState
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Dart异步"),
      ),
      body: Center(
        child: Material(
          child: InkWell(
            onTap: () {
              //main();
              //main1();
              // main2();
              // main4();

              //main5();

              //main6();

              main8();
            },
            child: Container(
              alignment: Alignment.center,
              height: 50,
              width: 200,
              decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(5),
                  gradient: LinearGradient(colors: [
                    Theme.of(context).primaryColor.withOpacity(.5),
                    Theme.of(context).primaryColor.withOpacity(.7),
                  ])),
              child: Text(
                "点击运行",
                style: TextStyle(color: Colors.white, fontSize: 16),
              ),
            ),
          ),
        ),
      ),
    );
  }

  main() {
    print("main---->start");
    print(doSomeThingNormal());
    print("main---->end");
  }

  main1() {
    print("main---->start");
    print(doSomeThingAsync());
    print("main---->end");
  }

  doThings() async {
    String msg = await doSomeThingAsync();
    print(msg);
  }

  main4() {
    print("main---->start");
    doThings();
    print("main---->end");
  }

  fun1() async {
    print("fun1---->start");
    String msg = await fun2();
    print("fun1:msg:\t${msg}");
  }

  fun2() async {
    print("fun2---->");
    return "Hello world";
  }

  main5() {
    print("main---->start");
    fun1();
    print("main---->end");
  }

  main6() {
    Future(() => print("立刻在Event queue中运行的Future"));
    Future.delayed(
        Duration(seconds: 2), () => print("2秒后在Event queue中运行的Future"));
    Future.microtask(() => print('在Microtask queue里运行的Future'));
    Future.sync(() => print('同步运行的Future'));
  }

  main7() {
    Future(() => print("立刻在Event queue中运行的Future")).then(fun1(), onError: (e) {
      handleError(e);
    }).then(fun2(), onError: (e) {
      handleError(e);
    });
  }

  main8() {
    // 实例化一个Completer
    var completer = Completer();
    // 这里可以拿到这个completer内部的Future
    var future = completer.future;
    future.then((_)=>print("then"));

    //...someThing elses
    completer.complete("finished");
  }

  doSomeThingNormal() {
    sleep(Duration(seconds: 2));
    return "Normal---->doSomeThing";
  }

  doSomeThingAsync() async {
    return Future<String>(() {
      sleep(Duration(seconds: 2));
      return "Async---->doSomeThing";
    });
  }

  main2() {
    //事件队列
    async_event();
    //微任务队列
    foo();
  }

  foo() {
    print("foo");
    scheduleMicrotask(foo);
  }

  async_event() async {
    print("normal_event");
  }

  //普通
  getNormalString() {
    return "normal---->string";
  }

  //异步
  getAsyncString() async {
    String msg = "async---->string";
    return msg;
  }

  void handleError(e) {
    print(e);
  }
}

上一篇下一篇

猜你喜欢

热点阅读