Dart 语言简介(二)-Flutter 异步编程

Dart类库有很是多的返回Future或者Stream对象的函数。 这些函数被称为异步函数:它们只会在设置好一些耗时操做以后返回,好比像 IO操做。而不是等到这个操做完成。编程

async和await关键词支持了异步编程,容许您写出和同步代码很像的异步代码。数组

Future:

Future.then 使用:

Future.delayed(new Duration(seconds:2),(){
      return 'hi World';
    }).then((data){
       print(data);
    });

成功以后代码 then 代码块缓存

Future 捕获异常(Future.catchError):

 

Future.delayed(new Duration(seconds:2),(){
      throw AssertionError("Error");
    }).then((data){
       print(data);
    }).catchError((e){
        print(e);
    });

执行过程当中发生错误执行 catchError代码块网络

使用then 的可选参数 onError 捕获错误信息:异步

Future.delayed(new Duration(seconds:2),(){
      throw AssertionError("Error");
    }).then((data){
       print(data);
    },onError: (e){
      print(e);
    });

Future.whenComplete:

有些时候,咱们会遇到不管异步任务执行成功或失败都须要作一些事的场景,好比在网络请求前弹出加载对话框,在请求结束后关闭对话框。这种场景,有两种方法,第一种是分别在then或catch中关闭一下对话框,第二种就是使用Future的whenComplete回调,async

Future.delayed(new Duration(seconds:2),(){
      throw AssertionError("Error");
    }).then((data){
       print(data);
    },onError: (e){
      print(e);
    }).whenComplete((){
      //不管成功或失败都会走到这里
    });

Future wait:

有些时候,咱们须要等待多个异步任务都执行结束后才进行一些操做,好比咱们有一个界面,须要先分别从两个网络接口获取数据,获取成功后,咱们须要将两个接
口数据进行特定的处理后再显示到UI界面上,应该怎么作?答案是Future.wait,它接受一个Future数组参数,只有数组中全部Future都执行成功后,才会触发then
的成功回调,只要有一个Future执行失败,就会触发错误回调。下面,咱们经过模拟Future.delayed 来模拟两个数据获取的异步任务,等两个异步任务都执行成功
时,将两个异步任务的结果拼接打印出来,异步编程

Future.wait(
      [
        Future.delayed(Duration(seconds: 2), () {
          return 'Hello1';
        }),
        Future.delayed(Duration(seconds: 2), () {
          return '-测试future Wait';
        })
      ],
    ).then((results) {
      print(results[0] + results[1]);
    }).catchError((e) {
      print(e);
    });

回调地狱(Callback Hell):

若是代码中有大量异步逻辑,而且出现大量异步任务依赖其它异步任务的结果时,必然会出现Future.then回调中套回调状况。举个例子,好比如今有个需求场景是用户先登陆,登陆成功后会得到用户ID,而后经过用户ID,再去请求用户我的信息,获取到用户我的信息后,为了使用方便,咱们须要将其缓存在本地文件系统,代码以下:函数

Future<String>  login(String userName,String pwd){
  
    //用户登陆
}

Future<String> getUserInfo(String id){
	
   //获取用户信息
}

Future saveUserInfo(String userInfo){
	
  //保存用户信息
}

接下来,执行整个任务流:测试

login("alice","******").then((id){
 //登陆成功后经过,id获取用户信息    
 getUserInfo(id).then((userInfo){
    //获取用户信息后保存 
    saveUserInfo(userInfo).then((){
       //保存用户信息,接下来执行其它操做
        ...
    });
  });
})

能够感觉一下,若是业务逻辑中有大量异步依赖的状况,将会出现上面这种在回调里面套回调的状况,过多的嵌套会致使的代码可读性降低以及出错率提升,而且很是难维护,这个问题被形象的称为回调地狱(Callback Hell)。回调地狱问题在以前JavaScript中很是突出,也是JavaScript被吐槽最多的点,但随着ECMAScript6和ECMAScript7标准发布后,这个问题获得了很是好的解决,而解决回调地狱的两大神器正是ECMAScript6引入了Promise,以及ECMAScript7中引入的async/await。 而在Dart中几乎是彻底平移了JavaScript中的这二者:Future至关于Promise,而async/await连名字都没改。接下来咱们看看经过Future和async/await如何消除上面示例中的嵌套问题code

使用Future消除Callback Hell:

login("alice","******").then((id){
      return getUserInfo(id);
}).then((userInfo){
    return saveUserInfo(userInfo);
}).then((e){
   //执行接下来的操做 
}).catchError((e){
  //错误处理  
  print(e);
});

使用async/await消除callback hell:

task() async {
   try{
    String id = await login("alice","******");
    String userInfo = await getUserInfo(id);
    await saveUserInfo(userInfo);
    //执行接下来的操做   
   } catch(e){
    //错误处理   
    print(e);   
   }  
}

async用来表示函数是异步的,定义的函数会返回一个Future对象,能够使用then方法添加回调函数。
await 后面是一个Future,表示等待该异步任务完成,异步完成后才会往下走;await必须出如今 async 函数内部。
能够看到,咱们经过async/await将一个异步流用同步的代码表示出来了

Stream:


Stream 也是用于接收异步事件数据,和Future 不一样的是,它能够接收多个异步操做的结果(成功或失败)。 也就是说,在执行异步任务时,能够经过屡次触发成功或失败事件来传递结果数据或错误异常。 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: (){

});