Dart中的异步编程(Future、scheduleMicrotask)

Dart是一门单线程的语言,Dart对异步操作对支持,可以使我们在编写Dart程序时可以异步的来执行耗时操作。这里的异步和多线程不是一个概念,在Dart中,异步但没有多线程,仍然是单线程。

Dart的事件循环(event loop)

在Dart中,实际上有两种队列:

  1. 事件队列(event queue),包含所有的外来事件:I/O、mouse events、drawing events、timers、isolate之间的信息传递。
  2. 微任务队列(microtask queue),表示一个短时间内就会完成的异步任务。它的优先级最高,高于event queue,只要队列中还有任务,就可以一直霸占着事件循环。microtask queue添加的任务主要是由 Dart内部产生。

因为 microtask queue 的优先级高于 event queue ,所以如果 microtask queue有太多的微任务, 那么就可能会霸占住当前的event loop。从而对event queue中的触摸、绘制等外部事件造成阻塞卡顿。

在每一次事件循环中,Dart总是先去第一个microtask queue中查询是否有可执行的任务,如果没有,才会处理后续的event queue的流程。


image.png

异步任务我们用的最多的还是优先级更低的 event queue。Dart为 event queue 的任务建立提供了一层封装,就是我们在Dart中经常用到的Future。

正常情况下,一个 Future 异步任务的执行是相对简单的:

  1. 声明一个 Future 时,Dart 会将异步任务的函数执行体放入event queue,然后立即返回,后续的代码继续同步执行。
  2. 当同步执行的代码执行完毕后,event queue会按照加入event queue的顺序(即声明顺序),依次取出事件,最后同步执行 Future 的函数体及后续的操作。
通过代码来看一下Future这个异步编程是啥样的(showCode)
void test1() {
  String a = 'A';
  print('最开始:$a');
  for (int i = 0; i < 10000000000; i++) {
    a = '网络请求';
  }

  print('结束:$a');
}

void test2() {
  String a = 'A';
  print('最开始a=:$a');
  Future(() {
    // 这里的耗时操作放到异步操作里去。  Future里的内容就是异步操作的
    for (int i = 0; i < 10000000000; i++) {
      a = '网络请求';
    }
    print('耗时操作最后a=:$a');
  });

  print('结束a=:$a');
}

void test3() async {
  // async和await要配套使用
  String a = 'A';
  print('最开始a=$a');
  await Future(() {
    // 通过await去修饰,那么主线程会等Future的异步操作结束后再继续往下走。
    for (int i = 0; i < 10000000000; i++) {
      a = '网络请求';
    }
  });
  print('结束a=$a');
}

void test4() {
  String a = 'A';
  print('最开始a=$a');
  Future future = Future(() {
    // Future通过返回一个Future对象可以来操作then,达到和test3一样的效果。
    for (int i = 0; i < 1000000000; i++) {
      a = '网络请求';
    }
    print('耗时操作里面的a=$a');
    return a;
  });

  future.then((onValue) {
    print('then里面的a=$a');
  });
}

void test5() {
  String a = 'A';
  print('最开始a=$a');
  Future(() {
    // Future通过链式调用的方式,推荐用这种方式。把需要在耗时操作后面执行的内容放到then里面去。
    // then里面的内容一样是异步。
    for (int i = 0; i < 1000000000; i++) {
      a = '网络请求';
    }
    print('耗时操作里面的a=$a');
    return a;
  }).then((onValue) {
    print('then里面的a=$a');
  });
}

void test6() {
  // 多个Future存在的时候,顺序是怎样的呢?

  print('主线程同步开始');

  Future(() {
    print('任务1开始');
    return '任务1';
  }).then((value) => print('$value结束了'));

  Future(() {
    print('任务2开始');
    return '任务2';
  }).then((value) => print('$value结束了'));

  Future(() {
    print('任务3开始');
    return '任务3';
  }).then((value) => print('$value结束了'));

  Future(() {
    print('任务4开始');
    return '任务4';
  }).then((value) => print('$value结束了'));

  Future(() {
    print('任务5开始');
    return '任务5';
  }).then((value) => print('$value结束了'));

  print('主线程同步结束');
}

void test61() {
  // Future 数组  多个Future还可以用数组的形式来表达

  Future.wait([
    Future(() {
      return '任务1';
    }),
    Future(() {
      return '任务2';
    }),
  ]).then((value) => print('任务执行结果为:${value[0]} 以及 ${value[1]}'));

  // 此时then里的内容会等Future数组里的2个异步任务都执行完毕了再执行。
}

void test7() {
  // 多个Future中还存在microtask(微任务)的时候,执行顺序又是如何

  print('主线程同步开始');

  /*
    1、Future的异步事件是按Future声明的顺序放入的event loop里的,then里的异步操作是放到微任务microtask loop 里面的。
    2、microtask loop队列里的异步事件的执行优先级比event loop异步队列里的优先级高
    3、每个loop time的时候都会先去microtask loop队列里看看当前是否有microtask未执行,有的话,优先执行microtask。
  */
  Future(() {
    print('任务1开始');
    scheduleMicrotask(() {
      // 这个microtask比then要晚执行,是因为他比then要晚放到microtask队列里。 then是在当前的Future申明的时候就放进去了,而这个
      // microtast 是Future执行的时候才放进去的,所以比then要晚。  注 then虽然可以理解是放到microtask队列里,但是它只有当前的Future执行完才会执行的。
      print('任务1里的微任务1开始');
    });
    return '任务1';
  }).then((value) => print('$value结束了'));

  Future(() {
    print('任务2开始');
    scheduleMicrotask(() {
      print('任务2里的微任务1开始');
    });
    return '任务2';
  }).then((value) => print('$value结束了'));

  Future(() {
    print('任务3开始');
    scheduleMicrotask(() {
      print('任务3里的微任务1开始');
    });
    return '任务3';
  }).then((value) => print('$value结束了'));

  Future(() {
    print('任务4开始');
    scheduleMicrotask(() {
      print('任务4里的微任务1开始');
    });
    return '任务4';
  }).then((value) => print('$value结束了'));

  Future(() {
    print('任务5开始');
    scheduleMicrotask(() {
      print('任务5里的微任务1开始');
    });
    return '任务5';
  }).then((value) => print('$value结束了'));

  scheduleMicrotask(() {
    print('这是微任务');
  });

  print('主线程同步结束');
}

void test8() {
  // 猜猜看,执行顺序是啥。。。
  Future x1 = Future(() => null);
  Future x = Future(() => print('1'));
  Future(() => print('2'));
  scheduleMicrotask(() => print('3'));
  x.then((value) => print('4'));
  print('5');
  x1.then((value) => print('6'));

  // 答案是:5、3、6、1、4、2
}

void test9() {
  // 猜猜看,执行顺序是啥。。。
  Future x1 = Future(() => null);
  x1.then((value) {
    print('6');
    scheduleMicrotask(() => print('7'));
  }).then((value) => print('8'));
  Future x = Future(() => print('1'));
  Future(() => print('2'));
  scheduleMicrotask(() => print('3'));
  x.then((value) => print('4'));
  print('5');

  // 答案是:5、3、6、8、7 、1、4、2
}

void test10() {
  //flutter的多线程 Isolate  执行顺序是无序的,且变量和外部的变量是隔离的。即便同名变量也是2个不同的变量
  // 好处是不用担心多线程访问对资源的抢夺。
  Isolate.spawn(fun11, 100);
  Isolate.spawn(fun12, 200);
  Isolate.spawn(fun11, 100);
  Isolate.spawn(fun12, 200);
  Isolate.spawn(fun11, 100);
  Isolate.spawn(fun12, 200);
  Isolate.spawn(fun11, 100);
  Isolate.spawn(fun12, 200);
  Isolate.spawn(fun11, 100);
  Isolate.spawn(fun12, 200);
  Isolate.spawn(fun13, {'name': '112'});
}

void fun11(int count) {
  print('fun11--$count');
}

void fun13(Map count) {
  print('fun11--$count');
}

void fun12(int count) {
  print('fun12--$count');
}

void test12() {
  print('函数主线程开始');
  // compute是一个多线程。
  compute(fun14, {'name': '112'}).then((value) => print(value));
  print('函数主线程结束');
}

Map fun14(Map count) {
  print('fun11--$count');
  return count;
}

参考引用了文章 Dart中的异步编程——Future、async和await

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,367评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,959评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,750评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,226评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,252评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,975评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,592评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,497评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,027评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,147评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,274评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,953评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,623评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,143评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,260评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,607评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,271评论 2 358