Flutter(二十二)-Dart中的异步编程Future

「这是我参与11月更文挑战的第20天,活动详情查看:2021最后一次更文挑战

今天这篇文章我们来介绍一下Dart中的异步编程;

我们先来看一段代码:

String _string = '默认';

void main() {
  getData();
  print('其他业务');
}

getData() {
  print('开始');
  for (int i = 0; i < 10000000000; i++) {
    _string = '耗时操作';
  }
  print('结束:$_string');
}
复制代码

其执行打印结果如下:

在这段代码中,for循环的耗时操作卡着了后续代码的执行;

那么如何想for循环变成异步操作呢?这个时候我们需要用到Future;

Future

FutureJavaScript中的Promise非常相似,表示一个异步操作的最终完成及其结果的表示。简单来说Future就是用来处理异步操作的,异步处理成功了就执行成功的操作,异步处理失败了就捕获错误或者停止后续操作。一个Future只会对应一个结果,要么成功,要么失败;

需要注意的是,Future的所有API的返回值仍然是一个Future对象,所以我们可以很方便的进行链式调用;

我们将代码修改如下:

String _string = '默认';

void main() {
  getData();
  print('其他业务');
}

getData() {
  print('开始:$_string');
  Future(() {
    for (int i = 0; i < 10000000000; i++) {
      _string = '耗时操作';
    }
    print('结束:$_string');
  });
}
复制代码

运行结果:

通过运行结果我们发现,将for循环放进Future中之后,耗时操作将不会阻塞代码的执行;

await/async

Flutter中还有一种方式可以让我们像写同步代码一样来执行异步任务而不用使用回调的方式,这就是async/await了,我们来看一段代码:

String _string = '默认';

void main() {
  getData();
  print('其他业务');
}

getData() async {
  print('开始:$_string');
  await Future(() {
    for (int i = 0; i < 1000000000; i++) {
      _string = '网络数据';
    }
    print('获取到数据:$_string');
  });
  print('结束:$_string');
}
复制代码

在此段代码中getData方法使用了async标识,而在方法内部for循环所在的Future添加了await标识,那么会有什么效果呢?

可以看到,Future之外的代码,等到Future中代码执行完毕之后才继续执行了;

那么,syncawait都是什么作用呢?

  • async用来表示函数是异步的,定义的函数会返回一个Future对象,可以使用then方法添加回调函数;
  • await后面是一个Future,表示等待该异步任务完成,异步完成后才会继续往下执行;await必须出现在async函数内部;

可以看到,我们通过使用asyncawait将一个异步流调用使用同步的代码逻辑表示了出来;

asyncawait只是一个语法糖,编译器或者解释器最终都会将其转化为一个Future的调用链;

then

那么,如果不使用await,我们能否实现同样的效果呢?

Future方法的返回值依然是一个Future对象,我们使用Future对象接收此返回值;然后通过future.then来打印一下返回的数据: 我们将getData方法修改如下:

getData() async {
  print('开始:$_string');
  Future future = Future(() {
    for (int i = 0; i < 1000000000; i++) {
      _string = '网络数据';
    }
    print('获取到数据:$_string');
  });
  future.then((value) {
    print('then方法: $_string');
  });
  print('结束:$_string');
}
复制代码

我们看一下打印结果:

我们可以看到,虽然没有了await标识,但是Future对象的then方法中依然可以获取到异步任务的数据;

但是此时then方法中的value却是空的:

image.png

这是因为,Future中捕获的异步任务没有返回值,那么我们给这个异步任务添加一个返回值,我们将getData方法修改如下:

getData() async {
  print('开始:$_string');
  Future future = Future(() {
    for (int i = 0; i < 1000000000; i++) {
      _string = '网络数据';
    }
    print('获取到数据:$_string');
    return '返回数据';
  });
  future.then((value) {
    print('then方法: $_string, value: $value');
  });
  print('结束:$_string');
}
复制代码

我们再来看一下打印结果:

此时,then方法中返回的value已经有值了,值为异步任务中return的返回值;在Future中返回的数据会被包装进Future的对象中,然后返回一个Future的对象;

catchError

很多情况下,我们在进行网络处理的时候的时候,会抛出异常,那么在Future中应该如何处理异常呢?

我们来看下边代码:

getData() async {
  print('开始:$_string');
  Future future = Future(() {
    for (int i = 0; i < 1000000000; i++) {
      _string = '网络数据';
    }
    throw Exception('网络异常');
  });
  future.then((value) {
    print('then方法: $_string, value: $value');
  });
  print('结束:$_string');
}
复制代码

Future内部,通过throw抛出了一个Exception,我们来看一下代码的执行结果:

image.png

可以看到,执行过程中代码报错,抛出了网络异常的错误信息;那么我们就需要进行拦截,将Exception捕获到,不能在执行中让工程出错,这个时候我们就需要使用catchError捕获异常,我们将代码修改如下:

image.png

运行结果:

image.png

我们确实捕获到了异常,但是工程依然报错了,这是为什么呢?这个时候,我们需要用到链式调用,在then方法之后,直接使用链式调用来捕获异常,代码如下:

image.png

运行结果:

image.png

onError

then的定义如下:

Future<R> then<R>(FutureOr<R> onValue(T value), {Function? onError});
复制代码

我们发现,在then方法中还有一个onError方法,此方法也可以用来处理错误以及异常:

image.png

运行结果:

image.png

可以看到,使用then中的onError方法捕获错误之后,catchError将不会调用;

此处的onError方法是在then内部定义的,所以需要在then方法内部调用,而非平级;

catchError是在整个Future的链式调用过程中捕获异常,而onError只在当前then中处理;我们在一个链式调用的过程中可能存在多个then

我们将catchErrorthen方法交换顺序:

image.png

运行结果:

image.png

我们发现,如果catchErrorthen方法前面,即使在catchError中捕获了异常,那么then方法依然会执行;

whenComplete

有时候,我们的异步任务无论成功与失败,都需要去做一些其他事情的时候,有两种方式处理*

  • then或者catchError中处理;
  • 使用FuturewhenComplete回调处理;

我们将代码修改如下:

image.png

运行结果:

image.png

虽然我们的whenComplete完成状态执行了,但是异常依然被抛出了;这里有两种方式处理:

  • whenComplete使用链式调用方式,不使用Future的对象调用;
  • whenComplete之后,再次捕获异常,代码如下:

image.png

我们一般使用过程中将then方法中放在前面,这样当异常捕获时,then方法将不会执行:

image.png

在项目中使用时,我们推荐以下写法:

image.png

推荐将各个状态的逻辑处理方法抽出,如下:

image.png

猜你喜欢

转载自juejin.im/post/7032546087210745864