flutter 异步 future,await,async 详解

294 阅读3分钟

并发

在应用中,所有的 Dart 代码都在 isolate 中运行。

每一个 Dart 的 isolate 都有独立的运行线程, 无法和其他线程共享对象, 只能用消息机制通信, dart应用默认使用一个isolate, 开发者可以创建多个isolate

dart是单线程+事件循环

1、当用户点击时,onPressed回调函数被放入事件循环中执行,执行的过程中发送了一个网络请求。

2、网络请求发出去后,该事件循环不会被阻塞,而是发现要执行的onPressed函数已经结束,会将它丢弃掉。

3、网络请求成功后,会执行then中传入的回调函数,这也是一个事件,该事件被放入到事件循环中执行,执行完毕后,事件循环将其丢弃。

作者:coderwhy

链接:juejin.cn/post/684490…

异步类和语法

Dart类库有非常多的返回Future或者Stream对象的异步函数

异步函数 : 调用方不会等到此函数操作完成后才返回, 调用方会获得一个future对象, 然后对其调用then()传入一个函数, 当future有结果时, 此函数被回调 (实际上是future有结果时将回调函数放到事件循环中)

一个最终会返回 int 类型值的 promise,应当声明为 Future

一个会持续返回一系列 int 类型值的 promise,应当声明为 Stream

Future类

factory Future(FutureOr<T> computation()) {}

当调用此工厂构造方法时, 传入的函数就会被一个_Future对象异步执行(类似java中将callble对象最为参数传入线程池的submit() )

  factory Future(FutureOr<T> computation()) {
    _Future<T> result = new _Future<T>();
    Timer.run(() {
      try {
        result._complete(computation());
      } catch (e, s) {
        _completeWithErrorCallback(result, e, s);
      }
    });
    return result;
  }
// -------- Future()中传入的函数默认是异步执行的, 示例如下
// 输出 :
// main start
// main end
// after sleep 3 s
void main() {
  print('main start');
  // 调用异步函数, 获得future对象
  var future = asyncFunc();
  // 调用then, 传入回调函数,
  future.then((s){
    print(s);
  });

  print('main end');
}
// 异步函数中返回future对象, 其中传入一个执行函数, 此函数返回FutureOr<T>对象
Future<String> asyncFunc(){
  return Future((){
    sleep(Duration(seconds: 3));
    return "after sleep 3 s";
  });
}

// -------- 可以用Future()控制代码执行顺序
void main() async {
  print('----main start');
  // 下面两行代码运行结果一样, 异步执行funA()然后异步执行funB()
  Future(funA).then((value) => funB());
  Future(funA).then((value) => Future(() => funB()));

  // 此时会等待funA()执行完后等待funB()执行
  await Future(funA);
  await Future(funB);

  // 由于dart默认在一个isolate中运行, 这样并不会加快运行时长, 仍是2s
  Future(funA);
  Future(funB);
  
  print('----main end');
}

void funA() {
  print("funA start...");
  sleep(Duration(seconds: 1));
  print("funA end...");
}

void funB() {
  print("funB start...");
  sleep(Duration(seconds: 1));
  print("funB end...");
}

async+await

使用async+await可以在调用异步函数时实现调用同步函数的效果

也就是主线程等待await修饰的异步函数返回后再执行下面的代码

await

代表等待future对象的结果, 加了await的函数会等待future运行结束并自动将返回值拆出来

加在Future对象或返回Future对象的函数前

只能在在async修饰的函数中使用await

/*
此代码的输出:
main start
asyncFunc : 3
asyncFunc : 2
asyncFunc : 1
ret type = String 
main : 3
main : 2
main : 1
main end
*/
void main() async {
  print('main start');
  
  //当调用asyncFunc()前面的await去掉时, ret的type是future<String> , 有await时, ret type=String
  var ret = await asyncFunc();

  print('ret type = ${ret.runtimeType}');
  sleepThreeSecond("main");
  print('main end');
}

Future<String> asyncFunc() async {
  sleepThreeSecond("asyncFunc");
  return "async func end";
}

void sleepThreeSecond(String tag) {
  var i = 3;
  while (i > 0) {
    sleep(Duration(seconds: 1));
    print('$tag : $i');
    i--;
  }
}

async

修饰的函数的返回值会自动被Future包裹, 注意并不是整个函数被Future()包裹

所以下面代码中ret的类型是Future<String>

void main() async{
  var ret = asyncFunc();
  print('$ret'); //output : Instance of 'Future<String>'
}

Future<String> asyncFunc()async{
    return "async func end";
}

错误处理

异步函数如果出现异常, 在then()后调用.catchError()传入异常回调函数来捕获

main(List<String> args) {
  print("main function start");
  var future = getNetworkData();
  future.then((value) {
    print(value);
  }).catchError((error) { // 捕获出现异常时的情况
    print(error);
  });
  print(future);
  print("main function end");
}