Flutter 中異步任務(wù)及多線程介紹



異常的捕獲

onError 與 catchError 的區(qū)別

getdata() async {
  Future future = Future(() {
    for (int i = 0; i < 10000000; i++) {}
    throw Exception('網(wǎng)絡(luò)異常');
  });

  // future.then((value) => print('value=$value'),
  //     onError: (e)=>print(e.toString()));
  
  future
      .then((value) => print('value=$value'))
      .catchError((e)=>print('使用的時(shí)候捕獲到了異常:' + e.toString()));
}

當(dāng)我們?cè)?Future 的閉包中拋出異常的時(shí)候可以使用 onError 或者 catchError 進(jìn)行捕獲異常并處理豪诲,它們的區(qū)別就是 onError 是寫在 then 里面的务甥,是針對(duì) then 這個(gè)事件。

catchError 的使用順序

雖然我們上面已經(jīng)通過(guò) catchError 捕獲了異常馍忽,但是當(dāng)我們后面再調(diào)用 whenComplete 的時(shí)候還需要再次調(diào)用 catchError迅矛,要不然程序還是會(huì)拋出異常妨猩。正確代碼如下。

future
      .then((value) => print('value=$value'))
      .catchError((e)=>print('使用的時(shí)候捕獲到了異常:' + e.toString()));

  future
      .whenComplete(() => print('完成了'))
      .catchError((e)=>print('完成的時(shí)候捕獲到了異常:' + e.toString()));

Future 鏈?zhǔn)秸{(diào)用

Future(() {
    for (int i = 0; i < 10000000; i++) {}
    throw Exception('網(wǎng)絡(luò)異常');
  })
      .then((value) => print('value=$value'))
      .whenComplete(() => print('完成了'))
      .catchError((e)=>print('捕獲到了異常:' + e.toString()));

針對(duì)以上代碼我們我們可以使用鏈?zhǔn)秸{(diào)用進(jìn)行簡(jiǎn)寫秽褒,但是需要注意一點(diǎn)的是 catchError 最好放到最后面壶硅。

代碼抽取

getdata() async {
  Future(() {
    for (int i = 0; i < 10000000; i++) {}
    throw Exception('網(wǎng)絡(luò)異常');
  })
      .then(thenFunc)
      .whenComplete(() => print('完成了'))
      .catchError((e)=>print('捕獲到了異常:' + e.toString()));
}

FutureOr thenFunc(Never value) {
  print('value=$value');
}

then 為例,當(dāng)我們閉包中代碼量比較多的時(shí)候销斟,我們可以在外面定義一個(gè)方法庐椒,對(duì)代碼進(jìn)行抽離。

多個(gè)異步任務(wù)的處理

Future 任務(wù)順序執(zhí)行

testFuture() async {
  Future(() {
    return '任務(wù) 1';
  }).then((value) => print('value=$value'));

  Future(() {
    sleep(Duration(seconds: 1));
    return '任務(wù) 2';
  }).then((value) => print('value=$value'));

  Future(() {
    return '任務(wù) 3';
  }).then((value) => print('value=$value'));
}

這里因?yàn)?Future 任務(wù)都是在同一個(gè)隊(duì)列中蚂踊,且 Flutter 是單線程约谈,所以這里任務(wù)的執(zhí)行順序是按照 Future 的添加順序執(zhí)行的,任務(wù)1 -> 任務(wù)2 -> 任務(wù)3

任務(wù)前后依賴

后面的任務(wù)依賴前一個(gè)任務(wù)的結(jié)果

Future(() {
    return '任務(wù) 1';
  }).then((value) {
    print('$value結(jié)束');
    return '任務(wù) 2';
  }).then((value) {
    print('$value結(jié)束');
    return '任務(wù) 3';
  });

當(dāng)我們后面的任務(wù)需要依賴前一個(gè)任務(wù)的執(zhí)行結(jié)果的時(shí)候棱诱,可以在 then 的閉包中執(zhí)行下一個(gè)任務(wù)泼橘,并把結(jié)果數(shù)據(jù)返回出去。

多個(gè)任務(wù)結(jié)束統(tǒng)一處理

Future.wait([
  Future(() {
    return '任務(wù) 1';
  }),
  Future(() {
  return '任務(wù) 2';
  }),
  Future(() {
  return '任務(wù) 3';
  })
  ]).then((value) => print(value[0] + value[1] + value[2]));

當(dāng)我們碰到這樣一個(gè)需求军俊,同時(shí)請(qǐng)求多個(gè)接口侥加,在這些接口都請(qǐng)求完成的時(shí)候統(tǒng)一處理,這時(shí)候我們就可以使用 Future.wait粪躬,Future.wait 里面是一個(gè)數(shù)組,可以放入多個(gè) Future 任務(wù)昔穴,在這些任務(wù)都執(zhí)行完畢的時(shí)候會(huì)調(diào)用 then 方法镰官,這時(shí)候 value 是一個(gè)數(shù)組,裝的是這幾個(gè) Future 任務(wù)的返回結(jié)果吗货。這里 value 數(shù)組的順序跟 Future 任務(wù)的順序是一樣的泳唠,且 Future 任務(wù)也是順序執(zhí)行的。

Dart 事件循環(huán)

通過(guò)上圖案例我們可以看到 scheduleMicrotask 中的任務(wù)會(huì)比 Future 中的任務(wù)先執(zhí)行宙搬,這里是因?yàn)樵?Dart 中有兩種隊(duì)列笨腥,事件隊(duì)列跟微任務(wù)隊(duì)列。

  • 事件隊(duì)列(event queue)勇垛,包含所有的外來(lái)事件:I/O脖母、mouse eventsdrawing events闲孤、timers谆级、isolate 之間的信息傳遞。
  • 微任務(wù)隊(duì)列(microtask queue)讼积,表示一個(gè)短時(shí)間內(nèi)就會(huì)完成的異步任務(wù)肥照。它的優(yōu)先級(jí)最高,高于 event queue勤众,只要隊(duì)列中還有任務(wù)舆绎,就可以一直霸占著事件循環(huán)。microtask queue 添加的任務(wù)主要是由 Dart 內(nèi)部產(chǎn)生们颜。

因?yàn)?microtask queue 的優(yōu)先級(jí)高于 event queue 吕朵,所以如果 microtask queue 有太多的微任務(wù), 那么就可能會(huì)霸占住當(dāng)前的 event loop掌桩。從而對(duì) event queue 中的觸摸边锁、繪制等外部事件造成阻塞卡頓。

在每一次事件循環(huán)中波岛,Dart 總是先去 microtask queue 中查詢是否有可執(zhí)行的任務(wù)茅坛,如果沒(méi)有,才會(huì)處理后續(xù)的 event queue 中的任務(wù)。

相關(guān)案例

  • 案例 1
Future f = Future(() => print('1'));
  Future(() => print('2'));
  scheduleMicrotask(() => print('3'));
  f.then((value) => print('4'));
  print('5');

這里打印的順序是 5贡蓖、3曹鸠、1、4斥铺、2彻桃。

  • 案例 2
Future f = Future(() => print('1'));
  Future(() => print('2'));
  scheduleMicrotask(() => print('3'));
  f.then((value) {
    print('4');
    scheduleMicrotask(() => print('5'));
  }).then((value) => print('6'));
  print('7');

這里打印的順序是 7、3晾蜘、1邻眷、4、6剔交、5肆饶、2,這里 6 相當(dāng)于 f.then 里面的任務(wù)岖常, then 中的任務(wù)相當(dāng)于被添加到了微任務(wù)隊(duì)列驯镊,所以 4、6 在 5 之前執(zhí)行竭鞍。

Dart 中的多線程 Isolate

  Isolate.spawn(func1, 10);
  Isolate.spawn(func2, 20);
  Isolate.spawn(func3, 30);

如上代碼中 func1 板惑、func2func3 會(huì)在子線程中執(zhí)行偎快,且是無(wú)序的冯乘。DartIsolate 更像是一個(gè)進(jìn)程,它有獨(dú)立的內(nèi)存空間滨砍,也就意味著每個(gè)進(jìn)程中的空間是獨(dú)立的往湿,所以不存在資源搶奪的問(wèn)題,所以不需要鎖惋戏,這樣的話用起來(lái)就非常便捷领追。但是也有一些需要注意的問(wèn)題,數(shù)據(jù)不能直接訪問(wèn)响逢,下面我們來(lái)看一下绒窑。

這里我們?cè)?func 中對(duì) a 的值進(jìn)行了修改,但是第二次打印的時(shí)候可以看到 a 的值還是 10舔亭。因?yàn)?func 中的 a 被獨(dú)立起來(lái)了些膨,與外部相互之間不能共享。如果想讓 funca 的值的修改能在外部起作用的話就需要用到端口钦铺。

int a = 10;

IsolateDemo() async {
  //創(chuàng)建 port
  ReceivePort port = ReceivePort();
  //創(chuàng)建 Isolate
  Isolate iso = await Isolate.spawn(func, port.sendPort);
  //通過(guò) port 監(jiān)聽數(shù)據(jù)變化
  port.listen((message) {
    a = message;
    print('接收到了:a = $a');
    
    //關(guān)閉端口
    port.close();
    iso.kill();
  });
}

func(SendPort send) {
  send.send(100);
  print('第一次打佣┪怼:a = $a');
}

這里我們可以定義一個(gè) port,在 func 中傳入 port.sendPort矛洞,這時(shí)候在 func 方法中發(fā)送數(shù)據(jù)洼哎,在 listen 閉包中就可以監(jiān)聽到。端口使用完畢后要調(diào)用 close 關(guān)閉端口,調(diào)用 kill 銷毀 Isolate噩峦。

computeDemo() async {
  int a = await compute(func1, 10);
}

int func1(int count) {
  return 100;
}

Dart 中使用多線程除了 Isolate锭沟,還有 computecompute 是基于 Isolate 的封裝识补,用法與 Isolate 類似族淮,有區(qū)別的就是 compute 可以接收函數(shù)中的返回值。

擴(kuò)展

pubspec.yaml 文件介紹

  • name:項(xiàng)目名稱凭涂,必填字段祝辣。
  • description:項(xiàng)目描述,非必填字段切油。
  • publish_to:代表要發(fā)布的平臺(tái)较幌,none 的話代表不發(fā)布。
  • version:工程的版本號(hào)白翻。
  • dependencies:可以設(shè)置 flutter 的版本,默認(rèn)是獲取最新版本绢片。
  • environment:可以指定 dart 版本的兼容范圍滤馍。
  • dev_dependencies:代表開發(fā)環(huán)境下的指定版本,打包的時(shí)候不會(huì)被打包底循。
  • flutter:字體以及圖片都是在 flutter 下面設(shè)置巢株。
  • dio: ^4.0.1: 以 dio 三方庫(kù)為例,^4.0.1 代表大版本區(qū)間不變的寫法熙涤,相當(dāng)于 >= 4.0.1, < 5.0.0阁苞。dio: 4.0.1 代表指定版本,dio: any 代表任意版本祠挫,dio:>3.0.1`` 代表版本號(hào)大于 3.0.1那槽。

import 介紹

import 'package:http/http.dart' as http;

http 為例,當(dāng)我們導(dǎo)入 import 的時(shí)候這里可以看到 as等舔,as 的作用就是給庫(kù)起別名骚灸,防止類名或者方法名沖突。導(dǎo)入庫(kù)的時(shí)候默認(rèn)是整個(gè)文件都導(dǎo)入慌植,關(guān)鍵字 show 代表只要導(dǎo)入的內(nèi)容甚牲,hide 代表不需要導(dǎo)入的內(nèi)容,我們可以根據(jù)需要進(jìn)行指定蝶柿。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末丈钙,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子交汤,更是在濱河造成了極大的恐慌雏赦,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異喉誊,居然都是意外死亡邀摆,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門伍茄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)栋盹,“玉大人,你說(shuō)我怎么就攤上這事敷矫±瘢” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵曹仗,是天一觀的道長(zhǎng)榨汤。 經(jīng)常有香客問(wèn)我,道長(zhǎng)怎茫,這世上最難降的妖魔是什么收壕? 我笑而不...
    開封第一講書人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮轨蛤,結(jié)果婚禮上蜜宪,老公的妹妹穿的比我還像新娘。我一直安慰自己祥山,他們只是感情好圃验,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著缝呕,像睡著了一般澳窑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上供常,一...
    開封第一講書人閱讀 51,462評(píng)論 1 302
  • 那天摊聋,我揣著相機(jī)與錄音,去河邊找鬼话侧。 笑死栗精,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瞻鹏。 我是一名探鬼主播悲立,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼新博!你這毒婦竟也來(lái)了薪夕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤赫悄,失蹤者是張志新(化名)和其女友劉穎原献,沒(méi)想到半個(gè)月后馏慨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡姑隅,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年写隶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片讲仰。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡慕趴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鄙陡,到底是詐尸還是另有隱情冕房,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布趁矾,位于F島的核電站耙册,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏毫捣。R本人自食惡果不足惜详拙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蔓同。 院中可真熱鬧溪厘,春花似錦、人聲如沸牌柄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)珊佣。三九已至,卻和暖如春披粟,著一層夾襖步出監(jiān)牢的瞬間咒锻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工守屉, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留惑艇,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓拇泛,卻偏偏與公主長(zhǎng)得像滨巴,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子俺叭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容