Dart中的异步编程(一):futures, async, await

一、为什么要用异步?

异步操作让您的程序在等待另一个操作完成的同时完成工作。
下面是一些常见的异步方式。

  • 通过网络获取数据。
  • IO操作

要在Dart中执行异步操作,可以使用Future类和async和 await 关键字

何为Future呢?

future表示异步操作的结果,可以有两种状态:uncompleted or completed.

  • uncompleted :当你调用异步函数时,当异步操作还没有完成时。
  • completed:异步操作结束,将会返回异步操作结果,或者抛出一个异常。

常用API

Future.then

我们使用Future.delayed 创建了一个延时任务(实际场景会是一个真正的耗时任务,比如一次网络请求),即2秒后返回结果字符串"Hello World",然后我们在then中接收异步结果并打印结果,代码如下:

main() {
  Future.delayed(Duration(seconds: 2), () {
    return "Hello World";
  }).then((result){
    print(result);
  });
}

Future.catchError

如果异步任务发生错误,我们可以在catchError中捕获错误.

main() {
  Future.delayed(new Duration(seconds: 2), () {
    //return "Hello World";
    throw Exception("error");
  }).then((data) {
    print("success");
  }).catchError((e) {
    print(e);
  });
}

出现异常后,then将不会回调, catchError回调函数将被调用。还有一种去捕获异常。
then方法还有一个可选参数onError,我们也可以它来捕获异常:

main() {
  Future.delayed(new Duration(seconds: 2), () {
    //return "Hello World";
    throw Exception("error");
  }).then((data) {
    print("success");
  }, onError: (e) {
    print(e);
  });
}

Future.whenComplete

无论异步任务执行成功或失败都需要做一些事的场景,比如在网络请求前弹出加载对话框,在请求结束后关闭对话框。

main() {
  Future.delayed(new Duration(seconds: 2), () {
    //return "Hello World";
    throw Exception("error");
  }).then((data) {
    //执行成功会走到这里
    print(data);
  }).catchError((e) {
    //执行失败会走到这里
    print(e);
  }).whenComplete(() {
    //无论成功或失败都会走到这里
    print("Complete");
  });
}

Future.wait

有些时候,我们需要等待多个异步任务都执行结束后才进行一些操作,比如从两个接口获取数据,再将数据合并后,才能展示在界面上。类似RxJava的merge

main() {
  Future.wait([
    // 2秒后返回结果
    Future.delayed(new Duration(seconds: 2), () {
      return "hello";
    }),
    // 3秒后返回结果
    Future.delayed(new Duration(seconds: 3), () {
      return " world";
    })
  ]).then((results) {
    print(results[0] + results[1]);
  }).catchError((e) {
    print(e);
  });
}

. 运行后,3秒后,将会打印 hello world

Future.wait,它接受一个Future数组参数,只有数组中所有Future都执行成功后,才会触发then的成功回调,只要有一个Future执行失败,就会触发错误回调,不会触发then回调。

Async/await

async和await关键字是Dart异步支持的一部分。他们允许你像写同步代码一样写异步代码和不需要使用Future接口。

Callback Hell(回调地狱)

了解 Async/await 之前,我们先解释何为Callback Hell(回调地狱)。

如果代码中有大量异步逻辑,并且出现大量异步任务依赖其它异步任务的结果时,必然会出现Future.then回调中套回调情况。
举个例子,比如现在有个需求场景是:

  1. 用户先登录,登录成功后会获得用户ID,
  2. 然后通过用户ID,再去请求用户个人信息,
  3. 获取到用户个人信息后,为了使用方便,我们需要将其缓存在本地文件系统。

首先我们先模拟创建出上面3个匿名函数来执行耗时操作,代码如下:

1:获取用户id

  var login = <String>({String usename, String password}) {
    return Future.delayed(Duration(seconds: 2), () {
      if (usename == '123' && password == "123") {
        print("获取用户id成功");
        return "12";
      } else {
        throw Exception('账号密码错误');
      }
    });
  };

2:获取用户信息

  var getUserInfo = <String>(String id) {
    return Future.delayed(Duration(seconds: 2), () {
      print("$id 获取用户信息成功");
      return "klaus";
    });
  };

3:保存用户信息

  var saveUserInfo = (String userInfo) {
    return Future.delayed(Duration(seconds: 2), () {
       return '$userInfo 保存用户信息';
    });
  };

接下来我们按常规的方式去执行:

  login(usename: '123', password: '123')
      .then((id) => getUserInfo(id)
          .then((userInfo) => saveUserInfo(userInfo))
          .then((value) => print(value))
          .catchError((e) {}))
      .catchError((e) {
    print(e);
  });

从上面的代码可以感受到,如果业务逻辑中有大量异步依赖的情况,将会出现上面这种在回调里面套回调的情况,过多的嵌套会导致的代码可读性下降以及出错率提高,并且非常难维护,这个问题被形象的称为回调地狱(Callback Hell)。

接下来我们看看通过Future和async/await如何消除上面示例中的嵌套问题。

使用Future消除Callback Hell

  login(usename: "123", password: "123").then((id) {
    return getUserInfo(id);
  }).then((userInfo) {
    return saveUserInfo(userInfo);
  }).then((e) {
    //执行接下来的操作
     print("保存用户信息success");
  }).catchError((e) {
    //错误处理
    print(e);
  });

Future 的所有API的返回值仍然是一个Future对象,所以可以很方便的进行链式调用,如果在then中返回的是一个Future的话,该future会执行,执行结束后会触发后面的then回调,这样依次向下,就避免了层层嵌套。这样看起来就清晰很多。

使用async/await消除callback hell

我们知道Kotlin的协程可以做到,像写同步代码一样去写异步代码,那Dart语言也有这种特性。这就要使用async/await了,可以像写同步代码那样来执行异步任务而不使用回调的方式。是不是很厉害。国际惯例,先上代码,再解释:

这里定义一个匿名函数,然后去执行它,

var task = ({String useName, String password}) async {
    String id = await login(usename: useName, password: password);
    String userInfo = await getUserInfo(id);
    return saveUserInfo(userInfo);
  };

  task(useName: '123', password: '123').then((result) {
    print('$result');
  }).catchError((e) {
    print('catchError $e');
  });
  • async用来表示函数是异步的,定义的函数会返回一个Future对象,可以使用then方法添加回调函数。
  • await 后面是一个Future,表示等待该异步任务完成,异步完成后才会往下走;await必须出现在 async 函数内部。
  • 若要定义异步函数,请在函数体之前添加 async 关键字。
  • 如果在Future返回函数发生错误,你可能想捕获错误。Async函数可以用try-catch捕获错误。因为被async定义的函数 返回值是一个Future函数,所以也可以在catchError回调中获取异常。
  • 你可以使用多个await表达式,保证一个await执行完成过后再执行下一个。

Dart中还有一种处理异步方式:Steam,下篇文章再来详细的讲解一下。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值