Dart基礎(九)-異步支持

1.簡介:

??Dart庫充滿了返回Future或Stream對象的函數(shù);這些函數(shù)都是異步的闸迷。這些函數(shù)在做一些可能耗時的操作(如I/O)時嵌纲,會立即返回一個Future或Stream對象,進行函數(shù)后面代碼的執(zhí)行腥沽,而不需要等待該操作完成后再執(zhí)行函數(shù)后面的代碼逮走。
??Dart中使用async``await關鍵字來實現(xiàn)異步編程,但是和Java今阳、OC等異步不同师溅,代碼類似于同步代碼,實際上是異步執(zhí)行的盾舌。

關鍵術語:
同步操作:同步操作阻止其他操作執(zhí)行墓臭,直到它完成。
同步函數(shù):同步函數(shù)只執(zhí)行同步操作妖谴。
異步操作:異步操作一旦啟動起便,就允許其他操作在它完成之前執(zhí)行。
異步函數(shù):一個異步函數(shù)至少執(zhí)行一個異步操作窖维,也可以執(zhí)行同步操作。

異步場景:

  • 獲取網(wǎng)絡數(shù)據(jù)(請求數(shù)據(jù)妙痹、圖片下載铸史、文件下載等);
  • 讀取數(shù)據(jù)(從文件怯伊、數(shù)據(jù)庫琳轿、磁盤中);
  • 寫入數(shù)據(jù)(文件耿芹、數(shù)據(jù)庫崭篡、磁盤等)。

2.處理Future

??Future表示一個不會立即完成的計算過程吧秕。與普通函數(shù)直接返回結果不同的是異步函數(shù)返回一個將會包含結果的Future琉闪。該 Future會在結果準備好時通知調用者。
future與Future:
??future(小寫“f”)是Future類的一個實例砸彬。future表示異步操作的結果颠毙,可以有兩種狀態(tài):未完成uncompleted或已完成completed斯入。
未完成uncompleted
??當調用一個異步函數(shù)時,它將返回一個未完成的future蛀蜜。該future表示函數(shù)正在等待函數(shù)的異步操作完成或拋出錯誤刻两。
成功的完成Completing with a value
??Future <T>類型的future以T類型的值結束。例如滴某,Future <String>類型的future產生一個字符串值磅摹。如果future沒有產生一個可用的值,那么future的類型是Future <void>霎奢。
錯誤的完成Completing with an error
??如果函數(shù)執(zhí)行的異步操作由于任何原因失敗户誓,則future函數(shù)將以錯誤的方式完成。

// 引入Future椰憋,并返回一個Future<void>的例子
Future<void> fetchUserOrder() {
  // Imagine that this function is fetching user info from another service or database.
  return Future.delayed(const Duration(seconds: 2), () => print('Large Latte'));
}

// 返回一個future錯誤的例子
Future<void> fetchUserOrder() {
// Imagine that this function is fetching user info but encounters a bug
  return Future.delayed(const Duration(seconds: 2),
      () => throw Exception('Logout failed: user ID is invalid'));
}

Futuer和future總結:

  • Future<T>實例產生類型T的值厅克;
  • 如果future沒有產生一個可用的值,那么future的類型是future <void>橙依;
  • future有兩種狀態(tài):未完成或已完成证舟;
  • 當調用一個返回future的函數(shù)時,該函數(shù)將會進入一個異步任務隊列中queues窗骑,并立即返回一個未完成狀態(tài)的future女责;
  • future的操作完成時,它會返回一個T類型的值或者一個錯誤创译。

3.異步async和等待await

??asyncawait關鍵字提供了一種聲明性的方式來定義異步函數(shù)并使用它們的結果抵知;使用的基本原則:

  • 要定義一個異步函數(shù),請在函數(shù)體前添加async软族;
  • await關鍵字只在異步函數(shù)中有效刷喜;
  • 同步函數(shù)中調用異步函數(shù),處理異步函數(shù)結果需要.then語法立砸。
Future<String> createOrderMessage() async {
  print("message star");
  var order = await fetchUserOrder3();
  print("message return");
  return 'Your order is: $order';
}

Future<String> fetchUserOrder3() async {
  // Imagine that this function is more complex and slow.
  print("order star");
  var result = await Future.delayed(
    const Duration(seconds: 2), () {
    return 'Large Latte';
  });
  print("order return");
  return result;
}

// 同步函數(shù)中調用
print('message top');
createOrderMessage().then((value) {
  print(value);
});
print('message bottom');

// 打印順序
flutter: message top
flutter: message star
flutter: order star
flutter: message bottom
flutter: order return
flutter: message return
flutter: Your order is: Large Latte

??如何解讀上述代碼的打印順序呢掖疮?

  • 同步函數(shù)中代碼是從上往下順序執(zhí)行的;
  • 異步函數(shù)中颗祝,可能是全部同步執(zhí)行浊闪,也可能有同步和也有異步執(zhí)行;
  • 異步函數(shù)中螺戳,以await關鍵字為分界線搁宾,await以上包含await調用函數(shù)的語句是同步的,await的返回結果和后面的代碼是異步執(zhí)行倔幼。
    示例如圖盖腿,紅色的代碼為同步執(zhí)行,綠色代碼為異步執(zhí)行的:
    異步函數(shù)的執(zhí)行.png

    ??開始解讀:
  1. 同步函數(shù)首先打臃锊亍:message top奸忽;
  2. 同步調用異步函數(shù)createOrderMessage堕伪,進入該函數(shù);
  3. 在函數(shù)createOrderMessage中栗菜,首先同步打忧反啤:message star;
  4. 同步調用await fetchUserOrder3(); 疙筹,進入函數(shù)fetchUserOrder3富俄;
  5. 在函數(shù)fetchUserOrder3中,首先同步打佣亍:order star霍比;
  6. 同步調用await Future.delayed ,進行函數(shù)Future.delayed調用暴备;
  7. 函數(shù)Future.delayed延遲執(zhí)行悠瞬,為異步調用,直接返回一個Future<String>的實例future涯捻,并加入異步隊列中等待執(zhí)行浅妆;
  8. 函數(shù)fetchUserOrder3檢測到異步執(zhí)行,直接返回一個future障癌,退出同步執(zhí)行凌外,將異步部分加入異步隊列中等待執(zhí)行;
  9. 函數(shù)createOrderMessage與6相同涛浙;
  10. 同步函數(shù)中異步函數(shù)createOrderMessage的同步部分執(zhí)行完畢康辑,繼續(xù)執(zhí)行下面同步代碼,打咏瘟痢:message bottom疮薇;
  11. 隊列要求先進先出,當最先加入異步隊列的任務已經(jīng)完成后我注,會以加入的次序執(zhí)行后續(xù)加入的異步代碼惦辛,后面的打印書序為:
    order return
    message return
    Your order is: Large Latte

3.異步錯誤error處理:

??在異步函數(shù)中,如同同步函數(shù)一樣使用try-catch處理出現(xiàn)的錯誤或者異常仓手。

Future<void> printOrderMessage() async {
  try {
    print('Awaiting user order...');
    var order = await fetchUserOrder();
    print(order);
  } catch (err) {
    print('Caught error: $err');
  }
}

Future<String> fetchUserOrder() {
  // Imagine that this function is more complex.
  var str = Future.delayed(
      const Duration(seconds: 4),
      () => throw 'Cannot locate user order');
  return str;
}

Future<void> main() async {
  // 會拋出Cannot locate user order異常
  await printOrderMessage();
}

4.處理流Streams

官方文檔
??Stream 是一系列異步事件的序列。其類似于一個異步的 Iterable玻淑,不同的是當你向 Iterable 獲取下一個事件時它會立即給你嗽冒,但是 Stream 則不會立即給你而是在它準備好時告訴你。
??Stream概要:

  • Stream提供一個異步的數(shù)據(jù)序列补履。
  • 數(shù)據(jù)序列包括用戶生成的事件和從文件讀取的數(shù)據(jù)添坊。
  • 你可以使用Stream API中的listen()方法和await for關鍵字來處理一個Stream
  • 當出現(xiàn)錯誤時箫锤,Stream提供一種處理錯誤的方式贬蛙。
  • Stream 有兩種類型:Single-SubscriptionBroadcast雨女。
4.1接收Stream事件:

??Stream可以通過許多方式創(chuàng)建,而這些所有的創(chuàng)建方式都可以相同的方式在代碼中使用:像使用for循環(huán) 迭代一個Iterable一樣阳准,我們可以使用 異步for循環(huán) (通常我們直接稱之為await for)來迭代Stream中的事件氛堕。例如:

Future<int> sumStream(Stream<int> stream) async {
  var sum = 0;
  await for (final value in stream) {
    sum += value;
  }
  return sum;
}

??上面代碼只是簡單地接收整型事件流中的每一個事件并將它們相加,然后返回(被Future包裹)相加后的整型值野蝇。當循環(huán)體結束時讼稚,函數(shù)會暫停直到下一個事件到達或Stream完成。內部使用await for循環(huán)的函數(shù)需要使用async關鍵字標記绕沈。
下面的示例中使用了async*函數(shù)生成一個簡單的整型Stream來測試上一個代碼片段:

Future<int> sumStream(Stream<int> stream) async {
  var sum = 0;
  await for (final value in stream) {
    sum += value;
  }
  return sum;
}

Stream<int> countStream(int to) async* {
  for (int i = 1; i <= to; i++) {
    yield i;
  }
}

// 調用
var stream = countStream(10);
var sum = sumStream(stream);
sum.then((value) {
  print(value);
});
4.2Stream錯誤處理:

??當Stream再也沒有需要處理的事件時會變?yōu)橥瓿蔂顟B(tài)锐想。與此同時,調用者可以像接收到新事件回調那樣接收Stream完成的事件回調乍狐。當使用await for循環(huán)讀取事件時赠摇,循環(huán)會在Stream完成時停止。

??有時在Stream完成前會出現(xiàn)錯誤浅蚪;比如從遠程服務器獲取文件時出現(xiàn)網(wǎng)絡請求失敗藕帜,或者創(chuàng)建事件時出現(xiàn) bug;在出現(xiàn)錯誤時掘鄙,需要將錯誤告知使用者耘戚。

??Stream可以像提供數(shù)據(jù)事件那樣提供錯誤事件。大多數(shù)Stream會在第一次錯誤出現(xiàn)后停止操漠,但其也可以提供多次錯誤并可以在在出現(xiàn)錯誤后繼續(xù)提供數(shù)據(jù)事件收津。在本篇文檔中我們只討論Stream最多出現(xiàn)并提供一次錯誤事件的情況。

??當使用await for讀取Stream時浊伙,如果出現(xiàn)錯誤撞秋,則由循環(huán)語句拋出,同時循環(huán)結束嚣鄙。你可以使用try-catch語句捕獲錯誤吻贿。下面的示例會在循環(huán)迭代到參數(shù)值等于 4 時拋出一個錯誤:

Stream<int> countStream(int to) async* {
  for (int i = 1; i <= to; i++) {
    if (i == 4) {
      throw Exception('Intentional exception');
    } else {
      yield i;
    }
  }
}

5.隔離區(qū)Isolates

??大多數(shù)計算機中,甚至在移動平臺上哑子,都在使用多核 CPU舅列。為了有效利用多核性能,開發(fā)者一般使用共享內存的方式讓線程并發(fā)地運行卧蜓。然而帐要,多線程共享數(shù)據(jù)通常會導致很多潛在的問題,并導致代碼運行出錯弥奸。

??為了解決多線程帶來的并發(fā)問題榨惠,Dart 使用isolate替代線程,所有的 Dart 代碼均運行在一個 isolate 中。每一個isolate有它自己的堆內存以確保其狀態(tài)不被其它 isolate 訪問赠橙。

??所有的 Dart 代碼都是在一個isolate中運行耽装,而非線程。每個 isolate 都有一個單獨的執(zhí)行線程期揪,并且不與其他的isolate共享任何可變對象掉奄。

你可以查閱下面的文檔獲取更多相關信息:

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市横侦,隨后出現(xiàn)的幾起案子挥萌,更是在濱河造成了極大的恐慌,老刑警劉巖枉侧,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件引瀑,死亡現(xiàn)場離奇詭異,居然都是意外死亡榨馁,警方通過查閱死者的電腦和手機憨栽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來翼虫,“玉大人屑柔,你說我怎么就攤上這事≌浣#” “怎么了掸宛?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長招拙。 經(jīng)常有香客問我唧瘾,道長,這世上最難降的妖魔是什么别凤? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任饰序,我火速辦了婚禮,結果婚禮上规哪,老公的妹妹穿的比我還像新娘求豫。我一直安慰自己,他們只是感情好诉稍,可當我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布蝠嘉。 她就那樣靜靜地躺著,像睡著了一般杯巨。 火紅的嫁衣襯著肌膚如雪是晨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天舔箭,我揣著相機與錄音,去河邊找鬼。 笑死层扶,一個胖子當著我的面吹牛箫章,可吹牛的內容都是我干的。 我是一名探鬼主播镜会,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼檬寂,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了戳表?” 一聲冷哼從身側響起桶至,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎匾旭,沒想到半個月后镣屹,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡价涝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年女蜈,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片色瘩。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡伪窖,死狀恐怖,靈堂內的尸體忽然破棺而出居兆,到底是詐尸還是另有隱情覆山,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布泥栖,位于F島的核電站簇宽,受9級特大地震影響,放射性物質發(fā)生泄漏聊倔。R本人自食惡果不足惜晦毙,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望耙蔑。 院中可真熱鬧见妒,春花似錦、人聲如沸甸陌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽钱豁。三九已至耻卡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間牲尺,已是汗流浹背卵酪。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工幌蚊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人溃卡。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓溢豆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親瘸羡。 傳聞我的和親對象是個殘疾皇子漩仙,可洞房花燭夜當晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內容

  • 概述 Dart的異步模型 Dart的異步操作(Future以及async、await) Dart的異步補充 一犹赖、D...
    IIronMan閱讀 437評論 2 1
  • Dart類庫有非常多的返回Future或者Stream對象的函數(shù)队他。 這些函數(shù)被稱為異步函數(shù):它們只會在設置好一些耗...
    seventhboy閱讀 1,204評論 0 2
  • [TOC] Dart基礎 在Dart中,所有能夠使用變量引用的都是對象object峻村,每個對象都是一個類class的...
    FreeRain77閱讀 891評論 0 1
  • Mixin 備注:子類沒有重寫超類A方法的前提下麸折,如果2個或多個超類擁有相同簽名的A方法,那么子類會以繼承的最后一...
    小流星雨閱讀 1,350評論 0 2
  • 學習筆記,旨在于快速入門和學習Dart雾棺,其中可能會有理解錯誤膊夹,請指出,一起學習捌浩。 系列文章 2.1放刨、Dart語言基...
    雙魚子曰1987閱讀 770評論 1 4