3.Dart中異步編程---Future

由于dart是一種單線程的語(yǔ)言,在編寫dart代碼時(shí)對(duì)于耗時(shí)操作的處理要使用異步來(lái)進(jìn)行。dart中自帶有future 渡处,async/awaitIsolatestream廣播等異步操作等方式。

Dart中的事件循環(huán)

dart中實(shí)際上有兩種隊(duì)列:

  • 1.事件隊(duì)列(event queue):包含所有的外來(lái)事件:I/O,mouse events, drawing events,timers,isolate之間的信息傳遞

  • 2.微任務(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)生

dart 事件循環(huán)流程圖

dart_loop_png.jpg

Future異步方式

常見處理耗時(shí)操作的返回值為future,不會(huì)阻塞當(dāng)前的線程導(dǎo)致程序卡死幢哨。

void testFuture(){
print("FutureTest==開始")
  Future((){
    return "任務(wù)1";
  }).then((value) => print(value+"結(jié)束"));

  Future((){
    return "任務(wù)2";
  }).then((value) => print(value+"結(jié)束"));

  Future((){
    sleep(Duration(seconds: 1));
    return "任務(wù)3";
  }).then((value) => print(value+"結(jié)束"));

  Future((){
    return "任務(wù)4";
  }).then((value) => print(value+"結(jié)束"));

  Future((){
    return "任務(wù)5";
  }).then((value) => print(value+"結(jié)束"));
 print("FutureTest==結(jié)束")
}
//打印結(jié)果雄人,輸出不會(huì)阻塞當(dāng)前線程
FutureTest==開始
FutureTest==結(jié)束
任務(wù)1結(jié)束
任務(wù)2結(jié)束
任務(wù)3結(jié)束
任務(wù)4結(jié)束
任務(wù)5結(jié)束

在future執(zhí)行過程中僚纷,future執(zhí)行的順序和添加的順序是一致的矩距,上一個(gè)任務(wù)執(zhí)行完成才會(huì)執(zhí)行下一個(gè)future,在OC/Swift多線程中這些返回的順序是不定的怖竭。

void testFuture1(){
  //future的執(zhí)行順序和添加順序有關(guān)锥债,在future中 then的比f(wàn)uture的默認(rèn)優(yōu)先級(jí)要高,執(zhí)行完了future直接執(zhí)行then
  Future((){
    return "任務(wù)1";
  }).then((value) => print(value+"結(jié)束"));

  Future x = Future((){
    return "任務(wù)2";
  })

  Future((){
    return "任務(wù)3";
  }).then((value) => print(value+"結(jié)束"));

  Future((){
    return "任務(wù)4";
  }).then((value) => print(value+"結(jié)束"));

  Future((){
    return "任務(wù)5";
  }).then((value) => print(value+"結(jié)束"));
  
  x.then((value)=>print(value+"結(jié)束"));
}

//執(zhí)行結(jié)果
任務(wù)1結(jié)束
任務(wù)2結(jié)束
任務(wù)3結(jié)束
任務(wù)4結(jié)束
任務(wù)5結(jié)束

future執(zhí)行完成返回的是一個(gè)future對(duì)象痊臭,使用then()方法注冊(cè)一個(gè)回調(diào)赞弥,F(xiàn)uture->then->Complete認(rèn)為是一個(gè)任務(wù),這個(gè)任務(wù)處理完成了才能繼續(xù)處理其他的任務(wù),future執(zhí)行任務(wù)中可以對(duì)error和complete事件做一個(gè)處理趣兄。

Future((){
    throw "發(fā)生錯(cuò)誤了";
    return "你好";
  }).then((value) => print("我就是value"))
      .catchError((error)=>print("當(dāng)前error==$error"))
      .whenComplete(() => print("當(dāng)前事件結(jié)束了"));

對(duì)于Future來(lái)說(shuō)绽左,異步處理成功了就執(zhí)行成功的操作,異步處理失敗了就捕獲錯(cuò)誤或者停止后續(xù)操作艇潭。一個(gè)Future只會(huì)對(duì)應(yīng)一個(gè)結(jié)果拼窥,要么成功,要么失敗蹋凝。Future.catchError回調(diào)只處理原始Future拋出的錯(cuò)誤鲁纠,不能處理回調(diào)函數(shù)拋出的錯(cuò)誤,onError只能處理當(dāng)前Future的錯(cuò)誤,Future.whenComplete 在Future完成之后總是會(huì)調(diào)用鳍寂,不管是錯(cuò)誤導(dǎo)致的完成還是正常執(zhí)行完畢改含。比如在網(wǎng)絡(luò)請(qǐng)求前彈出加載對(duì)話框,在請(qǐng)求結(jié)束后關(guān)閉對(duì)話框迄汛。并且返回一個(gè)Future對(duì)象.

請(qǐng)記住捍壤,Future的所有API的返回值仍然是一個(gè)Future對(duì)象,所以可以很方便的進(jìn)行鏈?zhǔn)秸{(diào)用鞍爱。

微任務(wù), 使用scheduleMicrotask()來(lái)創(chuàng)建一個(gè)微任務(wù)鹃觉,優(yōu)先級(jí)要比Future要高

//多種相互組合
void complexEventLoop(){
  print("開始執(zhí)行");
  Future(()=>"1").then((value) => print("$value==結(jié)束"));
  var x = Future(()=>"2");
  var y = Future(()=> "6");
  y.then((value) => print("$value==結(jié)束"));
  x.then((value){
    print("$value==結(jié)束");
    scheduleMicrotask((){
      print("4==結(jié)束");
    });
  }).then((value) => print("3==結(jié)束"));
  scheduleMicrotask((){
    //微任務(wù)隊(duì)列
    print("7==結(jié)束");
  });
  Future(()=> "5").then((value) => print("$value==結(jié)束"));
  print("結(jié)束執(zhí)行");
}
//輸出值
開始執(zhí)行
結(jié)束執(zhí)行
7==結(jié)束
1==結(jié)束
2==結(jié)束
3==結(jié)束
4==結(jié)束
6==結(jié)束
5==結(jié)束

結(jié)果分析

1.開始執(zhí)行和結(jié)束執(zhí)行是同步的,會(huì)按照順序輸出
2.任務(wù)7 是微任務(wù)優(yōu)先級(jí)最高睹逃,第一個(gè)輸出
3.接著按著Future加入的順序開始執(zhí)行 1 2
4.執(zhí)行到2結(jié)束的時(shí)候then中有一個(gè)微任務(wù)盗扇,但是改Future任務(wù)還沒有結(jié)束,會(huì)繼續(xù)執(zhí)行該任務(wù)輸出3
5.任務(wù)3輸出完成開始執(zhí)行微任務(wù) 輸出4
6.任務(wù)6對(duì)應(yīng)的Future先執(zhí)行沉填,輸出6
7.最后輸出5

Future提供了幾種方法實(shí)現(xiàn)

Future.value()疗隶,創(chuàng)建返回指定value的future

Future.value(1).then((value)=>print(value));
//輸出值
1

Future.delayed() 創(chuàng)建一個(gè)延遲執(zhí)行的future

Future.delayed(Duration(seconds: 1)).then((value) => print("延遲執(zhí)行"));
//輸出值
延遲執(zhí)行

Future.then() 用來(lái)注冊(cè)一個(gè)Future完成時(shí)要調(diào)用的回調(diào)。如果 Future 有多個(gè)then翼闹,它們也會(huì)按照鏈接的先后順序同步執(zhí)行斑鼻,同時(shí)也會(huì)共用一個(gè)event loop

Future((){
    return "任務(wù)5";
  }).then((value) => print(value+"結(jié)束"));
Future.timeout,超時(shí)會(huì)拋出TimeoutException異常處理
Future.delayed(new Duration(seconds: 2), () {
      return 1;
    }).timeout(new Duration(seconds:1)).then(print).catchError(print);
Future.foreach橄碾,根據(jù)某個(gè)集合創(chuàng)建一系列的Future卵沉,并且按著順序執(zhí)行這些Future颠锉,
Future.forEach({1,2,3}, (num){
      return Future.delayed(Duration(seconds: num),(){print("第$num秒執(zhí)行");});
    });

Future.wait(),等待多個(gè)Future任務(wù)完成,

  • 所有的future都有正常結(jié)果返回史汗,使用then監(jiān)聽的方法獲的是一個(gè)結(jié)果的集合琼掠,
  • 其中一個(gè)或者多個(gè)Future發(fā)生錯(cuò)誤,產(chǎn)生error停撞,則Future返回的結(jié)果就是第一個(gè)發(fā)生錯(cuò)誤的Future的值
void testFuture4() async {
  var future1 = new Future.delayed(new Duration(seconds: 1), () => 1);
  var future2 = new Future.delayed(new Duration(seconds: 2), () {
    return 5;
  });
  var future3 = new Future.delayed(new Duration(seconds: 3), () => 3);
  Future.wait([future1,future2,future3]).then((value) =>print(value))                                           
    .catchError((error)=>print(error));
 }
//輸出值
[1, 5, 3]

void testFuture5() async {
  var future1 = new Future.delayed(new Duration(seconds: 1), () => 1);
  var future2 = new Future.delayed(new Duration(seconds: 2), () {
    throw 'Future 發(fā)生錯(cuò)誤啦!';
  });
  var future3 = new Future.delayed(new Duration(seconds: 3), () => 3);
  Future.wait([future1,future2,future3]).then((value) =>print(value))                                           
    .catchError((error)=>print(error));
 }
//輸出值
Future 發(fā)生錯(cuò)誤啦!

Future.any,返回的是第一個(gè)執(zhí)行完成的Future的結(jié)果瓷蛙,不管結(jié)果是正確還是錯(cuò)誤

Future
    .any([1, 2, 5].map((delay) => new Future.delayed(new Duration(seconds: delay), () => delay)))
    .then(print)
    .catchError(print);
  }
///輸出值
1

Future.doWhile ,重復(fù)執(zhí)行某一個(gè)動(dòng)作,直到返回false或者future戈毒,退出循環(huán)艰猬,適用一些遞歸的操作

var random = new Random();
var totalDelay = 0;
Future.doWhile(() {
  if (totalDelay > 10) {
    print('total delay: $totalDelay seconds');
    return false;
  }
  var delay = random.nextInt(5) + 1;
  totalDelay += delay;
  return new Future.delayed(new Duration(seconds: delay), () {
    print('waited $delay seconds');
    return true;
  });
}).then(print).catchError(print);

Future.sync 同步執(zhí)行,然后調(diào)度到microtask queue來(lái)完成自己埋市,也就是一個(gè)阻塞任務(wù)冠桃,會(huì)阻塞當(dāng)前線程,sync執(zhí)行完成了之后代碼才會(huì)走到下一行

void testFuture() async {
    Future((){
        print("Future event 1");
    });
    Future.sync(() {
        print("Future sync event 2");
    });
    Future((){
        print("Future event 3");
    });
    Future.microtask((){
        print("microtask event");
    });
}
//輸出值
Future sync event 2
microtask event
Future event 1
Future event 3

如果在sync中返回一個(gè)future,執(zhí)行順序也會(huì)發(fā)生改變道宅。

void testFuture() async {
    Future((){
        print("Future event 1");
    });
    Future.sync(() {
        return Future(() => print("sync Future event 2"));
    });
    Future((){
        print("Future event 3");
    });
    Future.microtask((){
        print("microtask event");
    });
}
//輸出值
microtask event
Future event 1
sync Future event 2
Future event 3

Future.microtask 創(chuàng)建一個(gè)在microtask queue運(yùn)行的Future食听。

microtask queue的優(yōu)先級(jí)要比event queue高,而一般Future是在event queue執(zhí)行的污茵,所以Future.microtask創(chuàng)建的Future會(huì)優(yōu)先于其他的Future執(zhí)行:

void testFuture() async {
    Future((){
        print("Future event 1");
    });
    Future((){
        print("Future event 2");
    });
    Future.microtask((){
        print("microtask event");
    });
}
///輸出值
microtask event
Future event 1
Future event 2

async/await

默認(rèn)future是異步執(zhí)行的樱报,如果想要future同步執(zhí)行,可以通過async/await關(guān)鍵字:

void textAsyncAwait() async{
  String valuel = await Future((){
    return "這是就是value";
  });
  print("當(dāng)前value===$valuel");
  print("函數(shù)執(zhí)行完畢");
}
//輸出值
當(dāng)前value===這是就是value
函數(shù)執(zhí)行完畢

可以看到Future已經(jīng)開始同步執(zhí)行了泞当,await會(huì)等待Future執(zhí)行結(jié)束后才會(huì)繼續(xù)執(zhí)行后面的代碼迹蛤,

關(guān)鍵字async/await 是dart異步支持的一部分,是一個(gè)語(yǔ)法糖襟士,編譯器或解釋器最終都會(huì)將其轉(zhuǎn)化為一個(gè)Promise(Future)的調(diào)用鏈

  • async: 用來(lái)表示函數(shù)時(shí)異步的盗飒,定義的函數(shù)會(huì)返回一個(gè)Future對(duì)象
  • await:后面跟著一個(gè)Future,表示等待該異步任務(wù)完成敌蜂,異步任務(wù)完成后才會(huì)繼續(xù)往下執(zhí)行箩兽。await只能出現(xiàn)在異步函數(shù)內(nèi)部,能夠讓我們可以像寫同步代碼一樣來(lái)執(zhí)行異步任務(wù)章喉。

最后 Future的實(shí)現(xiàn),

查看future的源碼會(huì)發(fā)現(xiàn)身坐,異步任務(wù)是基于timer實(shí)現(xiàn)的,定時(shí)器設(shè)置的時(shí)間間隔為zero秸脱,result返回值then進(jìn)行回調(diào)注冊(cè)拿到執(zhí)行之后的結(jié)果 ,有興趣的朋友可以更深層的探究一下

factory Future(FutureOr<T> computation()) {
    _Future<T> result = new _Future<T>();
    Timer.run(() {
      try {
        result._complete(computation());
      } catch (e, s) {
        _completeWithErrorCallback(result, e, s);
      }
    });
    return result;
  }

正在學(xué)習(xí)dart中部蛇,如有不足或錯(cuò)誤的地方摊唇,歡迎指正。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末涯鲁,一起剝皮案震驚了整個(gè)濱河市巷查,隨后出現(xiàn)的幾起案子有序,更是在濱河造成了極大的恐慌,老刑警劉巖岛请,帶你破解...
    沈念sama閱讀 211,948評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旭寿,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡崇败,警方通過查閱死者的電腦和手機(jī)盅称,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)后室,“玉大人缩膝,你說(shuō)我怎么就攤上這事“杜” “怎么了疾层?”我有些...
    開封第一講書人閱讀 157,490評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)贡避。 經(jīng)常有香客問我痛黎,道長(zhǎng),這世上最難降的妖魔是什么贸桶? 我笑而不...
    開封第一講書人閱讀 56,521評(píng)論 1 284
  • 正文 為了忘掉前任舅逸,我火速辦了婚禮,結(jié)果婚禮上皇筛,老公的妹妹穿的比我還像新娘琉历。我一直安慰自己,他們只是感情好水醋,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評(píng)論 6 386
  • 文/花漫 我一把揭開白布旗笔。 她就那樣靜靜地躺著,像睡著了一般拄踪。 火紅的嫁衣襯著肌膚如雪蝇恶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,842評(píng)論 1 290
  • 那天惶桐,我揣著相機(jī)與錄音撮弧,去河邊找鬼。 笑死姚糊,一個(gè)胖子當(dāng)著我的面吹牛贿衍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播救恨,決...
    沈念sama閱讀 38,997評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼贸辈,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了肠槽?” 一聲冷哼從身側(cè)響起擎淤,我...
    開封第一講書人閱讀 37,741評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤奢啥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后嘴拢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體桩盲,經(jīng)...
    沈念sama閱讀 44,203評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評(píng)論 2 327
  • 正文 我和宋清朗相戀三年炊汤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了正驻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,673評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡抢腐,死狀恐怖姑曙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情迈倍,我是刑警寧澤伤靠,帶...
    沈念sama閱讀 34,339評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站啼染,受9級(jí)特大地震影響宴合,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜迹鹅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評(píng)論 3 313
  • 文/蒙蒙 一卦洽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧斜棚,春花似錦阀蒂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至义钉,卻和暖如春昧绣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背捶闸。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工夜畴, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人删壮。 一個(gè)月前我還...
    沈念sama閱讀 46,394評(píng)論 2 360
  • 正文 我出身青樓斩启,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親醉锅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評(píng)論 2 349

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