Flutter學(xué)習(xí)(三)異步線程

Dart類庫有非常多的返回Future或者Stream對象的函數(shù)得糜。 這些函數(shù)被稱為異步函數(shù):它們只會在設(shè)置好一些耗時(shí)操作之后返回,比如像IO操作教寂。而不是等到這個(gè)操作完成籍滴。
asyncawait關(guān)鍵詞支持了異步編程,允許您寫出和同步代碼很像的異步代碼贰镣。

*注:Dart語言并不存在異步線程呜象,asyncawait只是處理成很像異步的操作

1. Future

Future與JavaScript中的Promise非常相似膳凝,表示一個(gè)異步操作的最終完成(或失敗)及其結(jié)果值的表示恭陡。簡單來說蹬音,它就是用于處理異步操作的,異步處理成功了就執(zhí)行成功的操作休玩,異步處理失敗了就捕獲錯(cuò)誤或者停止后續(xù)操作著淆。一個(gè)Future只會對應(yīng)一個(gè)結(jié)果,要么成功拴疤,要么失敗永部。

由于本身功能較多,這里我們只介紹其常用的API及特性呐矾。還有苔埋,請記住,Future 的所有API的返回值仍然是一個(gè)Future對象蜒犯,所以可以很方便的進(jìn)行鏈?zhǔn)秸{(diào)用组橄。

(1)Future.then

為了方便示例,在本例中我們使用Future.delayed 創(chuàng)建了一個(gè)延時(shí)任務(wù)(實(shí)際場景會是一個(gè)真正的耗時(shí)任務(wù)罚随,比如一次網(wǎng)絡(luò)請求)玉工,即2秒后返回結(jié)果字符串"hi world!",然后我們在then中接收異步結(jié)果并打印結(jié)果淘菩,代碼如下:

Future.delayed(Duration(seconds: 2),(){
   return "hi world!";
}).then((data){
   print(data);
});
(2)Future.catchError

如果異步任務(wù)發(fā)生錯(cuò)誤遵班,我們可以在catchError中捕獲錯(cuò)誤,我們將上面示例改為:

Future.delayed(Duration(seconds: 2),(){
   //return "hi world!";
   throw AssertionError("Error");  
}).then((data){
   //執(zhí)行成功會走到這里  
   print("success");
}).catchError((e){
   //執(zhí)行失敗會走到這里  
   print(e);
});

在本示例中瞄勾,我們在異步任務(wù)中拋出了一個(gè)異常费奸,then的回調(diào)函數(shù)將不會被執(zhí)行,取而代之的是 catchError回調(diào)函數(shù)將被調(diào)用进陡;但是,并不是只有 catchError回調(diào)才能捕獲錯(cuò)誤微服,then方法還有一個(gè)可選參數(shù)onError趾疚,我們也可以用它來捕獲異常:

Future.delayed(Duration(seconds: 2), () {
    //return "hi world!";
    throw AssertionError("Error");
}).then((data) {
    print("success");
}, onError: (e) {
    print(e);
});
(3)Future.whenComplete

有些時(shí)候,我們會遇到無論異步任務(wù)執(zhí)行成功或失敗都需要做一些事的場景以蕴,比如在網(wǎng)絡(luò)請求前彈出加載對話框糙麦,在請求結(jié)束后關(guān)閉對話框。這種場景丛肮,有兩種方法赡磅,第一種是分別在thencatch中關(guān)閉一下對話框,第二種就是使用FuturewhenComplete回調(diào)宝与,我們將上面示例改一下:

Future.delayed(Duration(seconds: 2),(){
   //return "hi world!";
   throw AssertionError("Error");
}).then((data){
   //執(zhí)行成功會走到這里 
   print(data);
}).catchError((e){
   //執(zhí)行失敗會走到這里   
   print(e);
}).whenComplete((){
   //無論成功或失敗都會走到這里
});
(4)Future.wait

有些時(shí)候焚廊,我們需要等待多個(gè)異步任務(wù)都執(zhí)行結(jié)束后才進(jìn)行一些操作冶匹,比如我們有一個(gè)界面,需要先分別從兩個(gè)網(wǎng)絡(luò)接口獲取數(shù)據(jù)咆瘟,獲取成功后嚼隘,我們需要將兩個(gè)接口數(shù)據(jù)進(jìn)行特定的處理后再顯示到UI界面上,應(yīng)該怎么做袒餐?答案是Future.wait飞蛹,它接受一個(gè)Future數(shù)組參數(shù),只有數(shù)組中所有Future都執(zhí)行成功后灸眼,才會觸發(fā)then的成功回調(diào)卧檐,只要有一個(gè)Future執(zhí)行失敗,就會觸發(fā)錯(cuò)誤回調(diào)焰宣。下面泄隔,我們通過模擬Future.delayed 來模擬兩個(gè)數(shù)據(jù)獲取的異步任務(wù),等兩個(gè)異步任務(wù)都執(zhí)行成功時(shí)宛徊,將兩個(gè)異步任務(wù)的結(jié)果拼接打印出來佛嬉,代碼如下:

Future.wait([
  // 2秒后返回結(jié)果  
  Future.delayed(Duration(seconds: 2), () {
    return "hello";
  }),
  // 4秒后返回結(jié)果  
  Future.delayed(Duration(seconds: 4), () {
    return " world";
  })
]).then((results){
  print(results[0]+results[1]);
}).catchError((e){
  print(e);
});

執(zhí)行上面代碼,4秒后你會在控制臺中看到“hello world”闸天。

2. async/await

Dart中的async/await 和JavaScript中的async/await功能是一樣的:異步任務(wù)串行化暖呕。如果你已經(jīng)了解JavaScript中的async/await的用法,可以直接跳過本節(jié)苞氮。

(1)回調(diào)地獄(Callback Hell)

如果代碼中有大量異步邏輯湾揽,并且出現(xiàn)大量異步任務(wù)依賴其他異步任務(wù)的結(jié)果時(shí),必然會出現(xiàn)Future.then回調(diào)中套回調(diào)情況笼吟。舉個(gè)例子库物,比如現(xiàn)在有個(gè)需求場景是用戶先登錄,登錄成功后會獲得用戶ID贷帮,然后通過用戶ID戚揭,再去請求用戶個(gè)人信息,獲取到用戶個(gè)人信息后撵枢,為了使用方便民晒,我們需要將其緩存在本地文件系統(tǒng),代碼如下:

//先分別定義各個(gè)異步任務(wù)
Future<String> login(String userName, String pwd){
    ...
    //用戶登錄
};
Future<String> getUserInfo(String id){
    ...
    //獲取用戶信息 
};
Future saveUserInfo(String userInfo){
    ...
    // 保存用戶信息 
}; 

接下來锄禽,執(zhí)行整個(gè)任務(wù)流:

login("alice","******").then((id){
 //登錄成功后通過潜必,id獲取用戶信息    
 getUserInfo(id).then((userInfo){
    //獲取用戶信息后保存 
    saveUserInfo(userInfo).then((){
       //保存用戶信息,接下來執(zhí)行其他操作
        ...
    });
  });
})

可以感受一下沃但,如果業(yè)務(wù)邏輯中有大量異步依賴的情況磁滚,將會出現(xiàn)上面這種在回調(diào)里面套回調(diào)的情況,過多的嵌套會導(dǎo)致的代碼可讀性下降以及出錯(cuò)率提高宵晚,并且非常難維護(hù)垂攘,這個(gè)問題被形象的稱為回調(diào)地獄(Callback Hell)维雇。回調(diào)地獄問題在之前 JavaScript 中非常突出搜贤,也是 JavaScript 被吐槽最多的點(diǎn)谆沃,但隨著 ECMAScript 標(biāo)準(zhǔn)發(fā)布后,這個(gè)問題得到了非常好的解決仪芒,而解決回調(diào)地獄的兩大神器正是 ECMAScript6 引入了Promise唁影,以及ECMAScript7 中引入的async/await。 而在 Dart 中幾乎是完全平移了 JavaScript 中的這兩者:Future相當(dāng)于Promise掂名,而async/await連名字都沒改据沈。接下來我們看看通過Futureasync/await如何消除上面示例中的嵌套問題。

(2)消除回調(diào)地獄

消除回調(diào)地獄主要有兩種方式:

①饺蔑、使用Future消除Callback Hell

login("alice","******").then((id){
    return getUserInfo(id);
}).then((userInfo){
    return saveUserInfo(userInfo);
}).then((e){
   //執(zhí)行接下來的操作 
}).catchError((e){
  //錯(cuò)誤處理  
  print(e);
});

正如上文所述锌介, Future 的所有API的返回值仍然是一個(gè)Future對象,所以可以很方便的進(jìn)行鏈?zhǔn)秸{(diào)用”* 猾警,如果在then 中返回的是一個(gè)Future的話孔祸,該future會執(zhí)行,執(zhí)行結(jié)束后會觸發(fā)后面的then回調(diào)发皿,這樣依次向下崔慧,就避免了層層嵌套。

②穴墅、使用 async/await 消除 callback hell

通過Future回調(diào)中再返回Future的方式雖然能避免層層嵌套惶室,但是還是有一層回調(diào),有一種方式能夠讓我們可以像寫同步代碼那樣來執(zhí)行異步任務(wù)而不使用回調(diào)的方式玄货,這就要使用async/await了皇钞,下面我們先直接看代碼,然后再解釋松捉,代碼如下:

task() async {
   try{
    String id = await login("alice","******");
    String userInfo = await getUserInfo(id);
    await saveUserInfo(userInfo);
    //執(zhí)行接下來的操作   
   } catch(e){
    //錯(cuò)誤處理   
    print(e);   
   }  
}
  • async用來表示函數(shù)是異步的夹界,定義的函數(shù)會返回一個(gè)Future對象,可以使用 then 方法添加回調(diào)函數(shù)惩坑。

  • await 后面是一個(gè)Future掉盅,表示等待該異步任務(wù)完成,異步完成后才會往下走以舒;await必須出現(xiàn)在 async 函數(shù)內(nèi)部。

可以看到慢哈,我們通過async/await將一個(gè)異步流用同步的代碼表示出來了蔓钟。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市卵贱,隨后出現(xiàn)的幾起案子滥沫,更是在濱河造成了極大的恐慌侣集,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兰绣,死亡現(xiàn)場離奇詭異世分,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)缀辩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進(jìn)店門臭埋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人臀玄,你說我怎么就攤上這事瓢阴。” “怎么了健无?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵荣恐,是天一觀的道長。 經(jīng)常有香客問我累贤,道長叠穆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任臼膏,我火速辦了婚禮硼被,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘讶请。我一直安慰自己祷嘶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布夺溢。 她就那樣靜靜地躺著论巍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪风响。 梳的紋絲不亂的頭發(fā)上嘉汰,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天,我揣著相機(jī)與錄音状勤,去河邊找鬼鞋怀。 笑死,一個(gè)胖子當(dāng)著我的面吹牛持搜,可吹牛的內(nèi)容都是我干的密似。 我是一名探鬼主播,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼葫盼,長吁一口氣:“原來是場噩夢啊……” “哼残腌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤抛猫,失蹤者是張志新(化名)和其女友劉穎蟆盹,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體闺金,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逾滥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了败匹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寨昙。...
    茶點(diǎn)故事閱讀 38,814評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖哎壳,靈堂內(nèi)的尸體忽然破棺而出毅待,到底是詐尸還是另有隱情,我是刑警寧澤归榕,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布尸红,位于F島的核電站,受9級特大地震影響刹泄,放射性物質(zhì)發(fā)生泄漏外里。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一特石、第九天 我趴在偏房一處隱蔽的房頂上張望盅蝗。 院中可真熱鬧,春花似錦姆蘸、人聲如沸墩莫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽狂秦。三九已至,卻和暖如春推捐,著一層夾襖步出監(jiān)牢的瞬間裂问,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工牛柒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留堪簿,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓皮壁,卻偏偏與公主長得像椭更,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子蛾魄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評論 2 351

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