Flutter学习6- Dart异步,Http请求(待续..)及本地存储

强烈建议先看下这个 Dart异步编程

Future

Future表示在接下来的某个时间的值或者错误,Flutter中借助Future实现 Future有两种状态:

  • 执行中
  • 执行完成(包括成功和失败)

Future后续Api

先看看Future后续操作

main() {
  Future((){
    return '你好';//future返回了值
  }).then((value) {
     print(value);//then中接受到返回的值
     print('catchError');
  })//当then中接受onError(e){}时catchError不执行
  .catchError((e){//接受中间过程发生的异常
     print('catchError');
     })
     .whenComplete((){//失败成功后都会回调
     print('whenComplete');
    });

}
复制代码

结果

你好
catchError
whenComplete
复制代码

Future 常见api

Future.value()

返回一个携带值的Future对象

main() {
  print('start');
  Future.value('哈哈哈').then((value) => print(value));
  print('end');
}
复制代码

结果

start
end
哈哈哈
复制代码

Future.error() 创建一个以错误为完成状态的Future(可以携带任意类型值)

main() {
  print('start');
  Future.error(Exception("发生异常")).then((value) => print(value)).catchError((e){
    print(e.toString());
  });
  print('end');
}
复制代码

结果

start
end
Exception: 发生异常
复制代码

Future.delayed()延迟多长时间后执行(时间不一定准确,如果上个任务是个耗时操作,并不一定会是延迟的时间)

main() {
  print('start');
  Future.delayed(Duration(seconds: 2),(){
    return '延迟两秒';
  }).then((value){
     print(value);
     print(DateTime.now().second);
  }).catchError((e){
    print(e.toString());
  });
  print('end');
  print(DateTime.now().second);
}
复制代码

结果

start
end
11
延迟两秒
13
复制代码

如果在下面加上

  Future((){
    sleep(Duration(seconds: 5));
  }).then((value) => print(DateTime.now().second));
复制代码

结果是5s后执行

start
end
9
14
延迟两秒
14
复制代码

Future.wait([] futures)

等待所有的future完成后再一起返回,如果中间过程中又任意一个Future出错,则整个任务失败

main() {
  Future.wait([
    Future.value("哈哈哈"),
    Future.value(222),
    Future.delayed(Duration(seconds: 2))
    ])
    .then((value) => print(value));
}
复制代码

结果

[哈哈哈, 222, null]
复制代码

有一个Future出错

main() {
  Future.wait([
    Future.delayed(Duration(seconds: 2)),
    Future.error(222),
    ])
    .then((value) => print(value))
    .catchError((e){print("catchError");});
}
复制代码

整个任务失败

catchError
复制代码

Future.microtask

如果你看过了文章顶部的连接 应该知道 microtask 比event的优先级要高,当非future代码执行完成之后会有限轮训microtask的消息队列,处理其中的microtask再去轮训event

main() {
  Future((){print("0000000");}).then((value) => print('11111111'));
  Future.value('2222222').then((value) => print(value));
  Future.microtask(() =>print("3333333")).then((value) => print('444444444'));
}
复制代码

执行结果

2222222
3333333
444444444
0000000
11111111
复制代码

哎?为什么Future.value()比Future.microtask还快?看来下源码,有一句关键的

....
上面有一堆调用,省略...
  void _asyncCompleteWithValue(T value) {
    _setPendingComplete();
    _zone.scheduleMicrotask(() {
      _completeWithValue(value);
    });
  }
复制代码

所以Future.value()和Future.error(),相当于整了个microtask,所以...

Future.sync()

立马执行...跟同步代码一样

main() {
  Future((){print("0000000");}).then((value) => print('11111111'));
  Future.value('2222222').then((value) => print(value));
  Future.microtask(() =>print("3333333")).then((value) => print('444444444'));
  Future.sync(() => print("55555555"));
}
复制代码

结果

55555555
2222222
3333333
444444444
0000000
11111111
复制代码

当main函数开始执行时,1行:发送个event,2行发送个microtask,3行发送个microtask,4行立马执行,所以结果就是如上喽

Future.any([])

在Future的list中只要有一个Future执行完成返回了结果.then()中就是该Future的结果,如果list中出错的先完成 那也会回调到then()中

main() {
  Future.any([
    Future.value('0000000'),
    Future.value('11111111'),
    // Future.error("2222222")
    ])
    .then((value) => print(value));
}
复制代码

结果

0000000
复制代码

Future.forEach()

main() {
    Future.forEach([
    Future.error("2222222"),
    Future.value('0000000'),
    Future.value('11111111'),
    ], (element) => print(element)).catchError((e){
      print('catchError');
    }).whenComplete(() => print('whenComplete'));
}
复制代码

结果

Instance of 'Future<dynamic>'
Instance of 'Future<String>'
Instance of 'Future<String>'
Unhandled exception:
2222222
复制代码

Future.while()

当返回false是停止执行

main() {
    Future.doWhile((){
      print('1111111');
      Future.value('0000000');
    Future.value('11111111');
      return false;
    });
}
复制代码
1111111
复制代码

async 和await

async 和await 是为了方便Future相关Api的语法糖

  • async 标记的函数一般会返回future对象,
  • await 就是用同步方式的代码来执行异步操作
  • 使用await字段时函数必须加async字段

首先看一个微信登录的例子

main() {
    getWxInfo()
    .then((value) => getUserInfo(value))
    .then((value) => saveUserDate(value))
    .then((value) => print(value));
}

Future getWxInfo(){
  return Future.value("获得了微信UID");
}
Future getUserInfo(String param){
  return Future.value("$param->获得了用户信息");
}
Future saveUserDate(String param){
  return Future.value("$param->存储了了用户信息");
}
复制代码

结果

获得了微信UID->获得了用户信息->存储了了用户信息
复制代码

使用async和await 字段

main() async {
    String uid= await getWxInfo();
    String uinfo=await getUserInfo(uid);
    String save=await saveUserDate(uinfo);
    print(save);
}

Future getWxInfo(){
  return Future.value("获得了微信UID");
}
Future getUserInfo(String param){
  return Future.value("$param->获得了用户信息");
}
Future saveUserDate(String param){
  return Future.value("$param->存储了了用户信息");
}
复制代码

结果当然一样喽,这样就是使用同步代码的方式实现异步编程

单线程的Dart语言会不会卡?为什么现在感觉一点也不卡呢?

Dart的IO操作是交给运行时和系统,所在进行文件读写,网络请求时并不会卡住线程,当有大量的数据运算时比如复杂json数据解析,大量数据模型转换等场景下还是会卡的....

当然也是有解决办法的.....那就是 isolate(这部分还不理解,就不误人子弟了) 看下面链接

搞懂Dart异步并封装Isolate

Flutter中http请求

Dio项目介绍地址

Dio是目前比较牛批的一个第三方http库,很多地方借鉴了Okhttp的思想,比较容易上手,看下官网Api即可,等我深入理解这块内容之后 我再开源个基于Dio的封装吧

FutureBuilder

  1. 创建一个future对象(代码不是重点)
  Future<HomeTest> _post() async {
   Map<String ,dynamic> json= await HttpManager()
        .getAsync(
            url: '/posts/1',
            options:
                RequestOptions(baseUrl: 'https://jsonplaceholder.typicode.com'),
            responseFormat: null);

  return HomeTest.fromJson(json);
  }
复制代码
  1. 使用futurebuilder
Container(
    height: 800,
    child: FutureBuilder<HomeTest>(future: _post(),builder: (BuildContext context, AsyncSnapshot<HomeTest> snapshot) {
     if(snapshot.connectionState==ConnectionState.none){
          return Text('ConnectionState.none');
        }else if(snapshot.connectionState==ConnectionState.waiting){
           return Text('ConnectionState.waiting');
        }else if(snapshot.connectionState==ConnectionState.active){
           return Text('ConnectionState.active');
        }else{
		return Text('加载完成--${snapshot.data}');
              }
             },),
        )
复制代码

根据AsyncSnapshot执行的状态填充不同的内容,

但是有一个需要注意的是,如果父节点以及之上的节点调用了setState()方法在会从新加载Futurebuilder的post请求,就是重新创建了FutureBuilder,所以要控制好状态和位置

本地存储

在Flutter中在本地存储一些简单的key-value基于shared_preferences插件

shared_preferences项目地址

使用起来也比较简单

引入到项目,同步项目下载插件,导入相应的包即可使用

  _testSharedPreferences() async {
    var sp = await SharedPreferences.getInstance();
    await sp.setString("key1", 'value');
    String value1=sp.get('key1');
    String value2=sp.get('key2');
    print('value1=$value1;value2=$value2');
  } 
复制代码

需要注意的是:获取SharedPrefrences实例和setValue时是异步的,返回的是Future对象

分类:
Android
标签: