学习 Dart: 关于异步操作的高级知识(Async/Await)

1,676 阅读3分钟

简介

因为Dart是单线程的语言,所以如果线程中顺序执行的时候如果遇到一些耗时阻塞的操作,比如数据请求,延时操作等,就会产生卡顿,所以用异步来解决。 异步代码主要是用async await实现,熟悉js的同学应该非常熟悉,这里的使用方法也和js很像

异步 async 和 await

我们先来看一个例子:

void main(){
  getName1();
  getName2();
  getName3();
}

/**
* 注意事项
* 1、await关键字必须在async函数内部使用
* 2、await表达式可以使用多次
*/
Future getName1() async {
  await getStr1();
  await getStr2();
  print('getName1’);
}
getStr1() {
  print('getStr1’);
}
getStr2() {
  print('getStr2’);
}
getName2() {
  print('getName2’);
}
getName3() {
  print('getName3’);
}

运行结果如下:

getStr1
getName2
getName3
getStr2
getName1

我们来分析一下:

getName1 我们使用 async 做了标记,表示函数内部包含有延迟执行的代码,而await标记的方法就是需要延迟执行的,会将其放入延迟的队列中。getName2和getName3 我们并没有做类似的标记,说到这相信大家都明白运行结果的由来了

异步-then,catchError,whenComplete

void main() {
  new Future(() => futureTask())//异步任务的函数
      .then((m) => "result:$m")//任务执行完后的子任务
      .then((m) => m.length) //其中m为上个任务执行完后的返回的结果
      .then((m) => printLength(m))
	  .catchError(print)
      .whenComplete(() => whenTaskCompelete());//所有任务完成后的回调函数
}

如果需要监听“完毕”这个状态,那么用whenComplete,需要监听“成功”这个状态,用then,需要监听“失败”这个状态,用catchError。 如果重写了test方法,test返回true就可以在catchError的onError方法里捕获到异常,如果test返回false,就把该异常继续抛出而不会在catchError方法里被捕获,如果不写test默认实现一个返回true的test方法

异步-new Future()

void main(){
  testFuture();  /// f7 f1 f6 f3 f5 f2 f4
}
void testFuture() {
  Future f = new Future(() => print('f1'));
  Future f1 = new Future(() => null);
  //Future f1 = new Future.delayed(Duration(seconds: 1) ,() => null);
  Future f2 = new Future(() => null);
  Future f3 = new Future(() => null);
  f3.then((_) => print('f2'));
  f2.then((_) {
    print('f3');
    new Future(() => print('f4'));
    f1.then((_) {
      print('f5');
    });
  });
  f1.then((m) {
    print('f6');
  });
  print('f7');
}
  1. 使用new Future将任务加入event队列
  2. Future中的then并没有创建新的Event丢到Event Queue中,而只是一个普通的Function Call,在FutureTask执行完后,立即开始执行
  3. 如果在then()调用之前Future就已经执行完毕了,那么任务会被加入到microtask队列中,并且该任务会执行then()中注册的回调函数
  4. 使用Future.value构造函数的时候,就会上一条一样,创建Task丢到microtask Queue中执行then传入的函数
  5. Future.sync构造函数执行了它传入的函数之后,也会立即创建Task丢到microtask Queue中执行
  6. 当任务需要延迟执行时,可以使用new Future.delay()来将任务延迟执行

异步-scheduleMicrotask()

import 'dart:async';
void main(){
  testFuture();
}
void testScheduleMicrotask(){
  scheduleMicrotask(() => print('s1'));

  new Future.delayed(new Duration(seconds: 1), () => print('s2'));

  new Future(() => print('s3')).then((_) {
    print('s4');
    scheduleMicrotask(() => print('s5'));
  }).then((_) => print('s6'));

  new Future(() => print('s7'));

  scheduleMicrotask(() => print('s8'));

  print('s9');
}
  1. 如果可以,尽量将任务放入event队列中
  2. 使用Future的then方法或whenComplete方法来指定任务顺序
  3. 为了保持你app的可响应性,尽量不要将大计算量的任务放入这两个队列
  4. 大计算量的任务放入额外的isolate中

生成器-同步生成器

Main(){
  var it = getSyncGenerator(5).iterator;
  while (it.moveNext()) {
    print(it.current);
  }
}

Iterable<int> getSyncGenerator(int n) sync* {
  print('start');
  int k = 0;
  while (k < n) {
    yield k++;
  }
  print('end');
}
  1. 使用sync*,返回的是Iterable对象
  2. yield会返回moveNext为true,并等待 moveNext 指令
  3. 调用getSyncGenerator立即返回Iterable对象
  4. 调用moveNext方法时getSyncGenerator才开始执行

生成器-异步生成器

Main(){
//getAsyncGenerator(5).listen((value) => print(value));
  StreamSubscription subscription = getAsyncNumIterator(5).listen(null);
  subscription.onData((value) {
    print(value);
    if(value>=2){
      subscription.pause();
    }
  });
}

Stream<int> getAsyncGenerator(int n) async* {
  print('start');
  int k = 0;
  while (k < n) {
	yield k++;
  }
  print('end');
}
  1. 使用async*,返回的是Stream对象
  2. yield不用暂停,数据以流的方式一次性推送,通过StreamSubscription进行控制
  3. 调用getAsyncGenerator立即返回Stream,只有执行了listen,函数才会开始执行
  4. listen返回一个StreamSubscription 对象进行流监听控制
  5. 可以使用StreamSubscription对象对数据流进行控制