flutter异步相关

异步函数概念:

Dart类库有非常多的返回Future或者Stream对象的函数。 这些函数被称为异步函数

  • 一个Future只会对应一个结果,要么成功,要么失败。
  • 在Dart中Stream 也是用于接收异步事件数据,和Future 不同的是,它可以接收多个异步操作的结果,它常用于会多次读取数据的异步任务场景,如网络内容下载、文件读写、倒计时等。

Completer

用Future来处理一个异步操作,dart:async提供了Completer类,通过实例这个类生成Future,同时在用这个实例去控制生成的future的成功或者失败的状态。

1.类似javaScript的promise的参数函数的参数提供了resolve,reject方法去操控状态,completer.complete就相当于promise的resolve,completer.catchError相当于promise中的reject()

2.使用场景:一般如果你的异步操作是带回调的不能即时返回结果的类型

3.参考:flutter之Completer_yzpyzp的博客-CSDN博客

代码示例如下:

Future openImagePicker () {
    Completer completer = new Completer();
   
    // ImagePicker 是一个图片选择插件
    ImagePicker.singlePicker(
       context, 
       singleCallback: (data) {
         completer.complete(data);
       },
       failCallback:(err) {
         completer.catchError(err); 
       }
    );
     
    return completer.future;
}

// 使用
openImagePicker().then((data) {}).catchError((err){});

StreamController.broadcast()和StreamController()的区别

StreamController(),只能有一个监听,会缓存数据

StreamController.broadcast():可以有多个监听,不会缓存数据

 StreamController _streamController = StreamController.broadcast();

  @override
  void dispose() {
    _streamController.close();
    super.dispose();
  }

  @override
  void initState() {
    Future.delayed(Duration(seconds: 10), () {
      _streamController.stream.listen(
        (event) {
          MyLogUtil.d('stream:$event');
        },
        onError: (error) => MyLogUtil.d('ERROR:$error'),
        onDone: () => MyLogUtil.d('DONE'),
      );
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("stream的使用"),
      ),
      body: Center(
        child: Column(
          children: [
            ElevatedButton(
                onPressed: () => _streamController.sink.add(10),
                child: Text("10")),
            ElevatedButton(
                onPressed: () => _streamController.sink.add('hi'),
                child: Text("hi")),
            ElevatedButton(
                onPressed: () => _streamController.sink.addError('error'),
                child: Text("error")),
            ElevatedButton(
                onPressed: () => _streamController.sink.close(),
                child: Text("close")),
            StreamBuilder(
              stream: _streamController.stream
                  .where((event) => event > 5) //添加条件判断
                  .map((event) => event * 2) //
                  .distinct(), //去重,比如上面重复点击按钮,只会builder一次
              builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
                switch (snapshot.connectionState) {
                  case ConnectionState.none:
                    return Text('NONE:没有数据流');
                    break;
                  case ConnectionState.waiting:
                    return Text('WAITING:等待数据流');
                    break;
                  case ConnectionState.active:
                    if (snapshot.hasError) {
                      return Text('ACTIVE:错误:${snapshot.error}');
                    } else {
                      return Text('ACTIVE:正常:${snapshot.data}');
                    }
                    break;
                  case ConnectionState.done:
                    return Text('done:数据流关闭');
                    break;
                }

                return Container();
              },
            ),
          ],
        ),
      ),
    );
  }

常用的属性:(where、map、distinct)

      stream: _streamController.stream
                  .where((event) => event > 5)//添加条件判断
                  .map((event) => event * 2)//
                  .distinct(),//去重,比如上面重复点击按钮,只会builder一次

异步数据更新UI:

StreamBuilder是用于配合Stream来展示流上事件(数据)变化的UI组件,凡是UI会依赖多个异步数据而发生变化的场景都可以使用StreamBuilder
FutureBuilder展示异步任务状态,会依赖一个Future,它会根据所依赖的Future的状态来动态构建自身

async await

await的操作,不会影响方法外后续代码的执行;只会阻塞async方法内的后续代码

async、await原理:之所以说async/await是假异步,是因为他在执行过程中并没有开启新的线程更没有并发执行,而是通过单线程上的任务调度(协程,没有并发执行功能)实现的:
当代码执行到async则表示进入一个协程,会同步执行async的代码块。async的代码块本质上也相当于一个函数,并且有自己的上下文环境。当执行到await时,则表示有任务需要等待,CPU则去调度执行其他IO,也就是后面的代码或其他协程代码。过一段时间CPU就会轮询一次,看某个协程是否任务已经处理完成,有返回结果可以被继续执行,如果可以被继续执行的话,则会沿着上次离开时指针指向的位置继续执行,也就是await标志的位置。

由于并没有开启新的线程,只是进行IO中断改变CPU调度,所以网络请求这样的异步操作可以使用async、await,但如果是执行大量耗时同步操作的话,应该使用isolate开辟新的线程去执行。
 

例子1

_testAsyncKeyword() {
    print("test函数开始了:${DateTime.now()}");
    _testString().then((value) => print(value));
    print("test函数结束了:${DateTime.now()}");
  }
 
  Future<String> _testString() async {
    Future f = Future.delayed(Duration(milliseconds: 300), () {
      return "我是测试字符串===1";
    });
    String result = await f;
    print("我是测试字符串===2");
    return result;
  }
// flutter: test函数开始了:2021-05-27 13:58:56.595964
// flutter: test函数结束了:2021-05-27 13:58:56.598801
// flutter: 我是测试字符串===2
// flutter: 我是测试字符串===1

在代码示例中,执行到_testString()方法,会同步进入方法内部进行执行,当执行到await时就会停止async内部的执行,从而继续执行外面的代码。所以await的操作,不会影响后面代码的执行("test函数结束了"会先于_testString()内部打印)。
当await有返回后,会继续从await的位置继续执行(所以先打印出了 "我是测试字符串===2" ,然后返回Future的结果,并通过print打印出 "我是测试字符串===1")。

例子2

_testAsyncKeyword() async {
    print("test函数开始了:${DateTime.now()}");
    print(await _testString());
    print("test函数结束了:${DateTime.now()}");
  }
 
  Future<String> _testString() async {
    Future f = Future.delayed(Duration(milliseconds: 300), () {
      return "我是测试字符串===1";
    });
    String result = await f;
    print("我是测试字符串===2");
    return result;
  }
 
// flutter: test函数开始了:2021-05-27 14:06:48.185704
// flutter: 我是测试字符串===2
// flutter: 我是测试字符串===1
// flutter: test函数结束了:2021-05-27 14:06:48.497481
 

在代码示例中, _testAsyncKeyword() 本身内部就有一个await操作,当执行到await时就会停止_testAsyncKeyword() async内部的执行.等待_testString()有结果返回之后,继续执行.

_testString()内部也是有一个await操作,当执行到await时就会停止_testString() async内部的执行,等待300毫秒,Future有结果后,打印字符串2

_testAsyncKeyword() 继续执行 打印 字符串1 及 结束

例子3

_testAsyncKeyword() {
    print("test函数开始了:${DateTime.now()}");
    firstString().then((value) => print(value));
    secondString().then((value) => print(value));
    thirdString().then((value) => print(value));
    print("test函数结束了:${DateTime.now()}");
  }
 
_testKeyword2() async{
    print("test函数开始了:${DateTime.now()}");
    print(await firstString());
    print(await secondString());
    print(await thirdString());
    print("test函数结束了:${DateTime.now()}");
  }
Future<String> firstString() {
    return Future.delayed(Duration(milliseconds: 300), () {
      return "我是一个字符串";
    });
  }
 
  Future<String> secondString() {
    return Future.delayed(Duration(milliseconds: 200), () {
      return "我是二个字符串";
    });
  }
 
  Future<String> thirdString() {
    return Future.delayed(Duration(milliseconds: 100), () {
      return "我是三个字符串";
    });
  }
 
//_testAsyncKeyword() 的打印:
//flutter: test函数开始了:2021-05-27 14:17:42.620409
//flutter: test函数结束了:2021-05-27 14:17:42.624359
//flutter: 我是三个字符串
//flutter: 我是二个字符串
//flutter: 我是一个字符串
 
//_testKeyword2() 的打印:
//flutter: test函数开始了:2021-05-27 14:18:41.401992
//flutter: 我是一个字符串
//flutter: 我是二个字符串
//flutter: 我是三个字符串
//flutter: test函数结束了:2021-05-27 14:18:42.027575
 

参考:flutter async和await原理解析(await阻塞当前方法,不阻塞方法外的代码继续执行)_flutter await-CSDN博客

Dart中async和async*的区别:

==================Dart中async和async*的区别start============================

async和await

这两个关键字的使用只需要记住两点:
    只有async方法才能使用await关键字调用方法
    如果调用别的async方法必须使用await关键字
async是让方法变成异步。
await是等待异步方法执行完成

Dart中async和async*有什么区别?

  • async返回Future.
  • async*返回Stream.

1.async

这是异步调用。当一个函数被标记成async的时候,意味这个方法可能要从事耗时工作,比如说网络请求、处理图片等等。被async标记的方法会把返回值用Future包裹一下。

Future<int> doSomeLongTask() async {
  await Future.delayed(const Duration(seconds: 1));
  return 42;
}

我们可以通过await来获取Future里的返回值:

main() async {
  int result = await doSomeLongTask();
  print(result); // 等待一分钟后打印 '42'
}

2.async*

async*比async多了一个*,加上*其实是函数生成器的意思。被async*标记的函数会在返回一组返回值,这些返回值会被包裹在Stream中。async*其实是为yield关键字发出的值提供了一个语法糖。

Stream<int> countForOneMinute() async* {
  for (int i = 1; i <= 60; i++) {
    await Future.delayed(const Duration(seconds: 1));
    yield i;
  }
}

上面的其实就是异步生成器了。我们可以使用yield替代return返回数据,因为这个是时候我们的函数还在执行中。此时,我们就可以使用await for去等待Stream发出的每一个值了。

main() async {
  await for (int i in countForOneMinute()) {
    print(i); // 打印 1 到 60,一个秒一个整数
  }
}

案例:我们需要每一秒钟请求一次接口,一共请求10次,来看看京东还剩多少茅台。

getMaoTai() async{
    for (int i = 0; i <10; i++){
      await Future.delayed(Duration(seconds: 1), ()async {
        MaoTaiData data = await fetchMaoTaiData();
        setState(){
          //更新UI
        };
      });
  }

上面的代码里使用了循环,然后每一秒钟请求依次接口,返回数据后调用setState()更新UI。这样做会导致你每隔一两秒就setState()一次,如果不怕性能问题,不怕产品经理打你,你这么玩玩。这个时候async*就应该上场了:

 Stream<MaoTaiData> getData() async* {
      for (int i = 0; i <10; i++) {
        await Future.delayed(Duration(seconds: 1));
        yield await fetchMaoTaiData();
      }
    }

这样我们就可以使用StreamBuilder包裹下Widget,就不必每次都去setState()了。

==================Dart中async和async*的区别end==============================

多接口请求返回后统一处理

1.Future.wait([response1, response2])结合Completer实现

Future<void> fetchData() async {
  try {
    var completer = Completer<List<dynamic>>();
    var response1 = http.get(Uri.parse('https://example.com/data1'));
    var response2 = http.get(Uri.parse('https://example.com/data2'));
    Future.wait([response1, response2]).then((results) {
      var data1 = jsonDecode(results[0].body);
      var data2 = jsonDecode(results[1].body);
      completer.complete([data1, data2]);
    });
    var results = await completer.future;
    print(results[0]);
    print(results[1]);
  } catch (e) {
    print('Error: $e');
  }
}

2.Future.wait([response1, response2])结合FutureBuilder实现

Future fetchData() async {
  var response1 = http.get(Uri.parse('https://example.com/data1'));
    var response2 = http.get(Uri.parse('https://example.com/data2'));
   return Future.wait([response1, response2]);
}

 FutureBuilder(
        future: fetchData(),
        builder: (context, AsyncSnapshot snapshot) {
        
        if (snapshot.connectionState == ConnectionState.done) {
            // 说明有数据
            ...
        }
      })

3.自定义zipWith 扩展方法,返回多个值,使用的是dart3.0新特性Record

Flutter - Dart 3α 新特性 Record 和 Patterns 的提前预览讲解 · GitBook

Future<void> main() async {
//返回两个值
final (name, year) = await Future.value("andrew")
    .zipWith(Future.value(1984));
print('name:$name ,year:$year');
  
//打印值:name:andrew ,year:1984 

  
//返回三个值
final ((name, year), married) = await Future.value("andrew")
      .zipWith(Future.value(1984))
      .zipWith(Future.value(false));
  print('name:$name ,year:$year , married:$married');
  
//打印值:name:andrew ,year:1984 , married:false

}

extension FutureZipX<T> on Future<T> {
  Future<(T, T2)> zipWith<T2>(Future<T2> future2) async {
    late T result1;
    late T2 result2;
    await Future.wait(
        [then((it) => result1 = it), future2.then((it) => result2 = it)]);
    return (result1, result2);
  }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值