Dart 異步編程相關概念簡述

​ 學習 Dart 的異步編程時,須要對異步編程所涉及的相關知識體系進行梳理,咱們可根據如下幾個發問來逐個瞭解異步編程涉及的內容:數組

  • 爲何須要異步編程?
  • 異步編程的內在機制是什麼?
  • Dart 中如何進行異步編程?

isolate:

Dart是單「線程」語言:異步

Dart 代碼在某個 isolate 的上下文中運行,該 isolate 擁有 Dart 代碼所需的全部內存。當Dart 代碼正在執行時,同一個 isolate 中的其餘代碼都沒法運行,更通俗地講,Dart 一次執行一個操做,這意味着只要一個操做正在執行,它就不會被任何其餘 Dart 代碼中斷。這就引起出如下問題:async

1,若是 Dart 代碼正在執行長耗時計算,或者等待 I/O,將會致使程序凍結;ide

2,若是須要同時運行不一樣的 Dart 代碼,就須要將這些代碼放在不一樣的 isolate 環境中執行 ;異步編程

3, 如何在 Dart 中編寫異步代碼,進行異步操做?(所謂異步操做,指的是當你的程序在等待其它操做完成時,可讓其先完成其它部分的操做)oop

咱們先放碼出來:學習

main() {
  fallInLove();//談戀愛
  House newHourse = buyHouse();//買房(耗時、費力操做)
  livingHouse(newHourse);//買完房子以後住進新房
  marry();//結婚
  haveChild();//生娃
}

如上所示,在這種狀況下,只有當第一條語句執行完以後,後面的語句才能接着按順序執行;ui

大部分人的人生三件事:買房,結婚,生娃線程

這三件大事,若是用 Dart 語言來實現:

若是是在同步操做中,而且家裏沒礦,六個錢包也湊不齊首付的話,你的人生就會凍結- -等着買完房以後,才能結婚,結完婚以後才能生娃;

有沒有什麼操做能夠實現先結婚,再生娃,再買房呢?答案是有的:若是將買房(buyHouse())放在異步操做裏,這時候你要給你的丈母孃及老婆一個承諾:之後有錢了再買房!如今先結婚、生娃!詳情咱們後文再敘,這裏先提出這麼個設想;

event loop:

當開始運行 Flutter 或者 Dart 應用程序時,一個 isolate 就會被建立並啓動,當 isolate 建立成功時,Dart 就會進行以下三個事項:

1,初始化兩個先進先出(FIFO)隊列:一個稱爲 MicroTask 隊列,另一個稱爲 Event 隊列;

2,執行main()方法,且執行完成後,進入3

3,啓動 Event Loop

4,開始處理兩個隊列中的元素(兩個隊列的執行有前後順序,見後文)

也就是說,每一個 isolate 中都會有且只有一個Event Loop(事件循環)和兩個隊列(MicroTask Queue、Event Queue ); Event Loop 將根據MicroTask隊列和Event隊列裏的內容來驅動代碼的執行方式和順序。

Add the main isolate executing tasks off the queue: main(), then key event handler, then click event handler, then timer task, etc.

那麼問題來了:

1,Event Loop是什麼?用來幹啥的?

2,MicroTask隊列和Event隊列都分別是什麼?有什麼用?

3,二者有什麼區別?

Event Loop是一個按期喚醒的無限循環:它在MicroTask、Event隊列中查找是否有須要運行的任務。若是隊列中的任務存在,則當且僅當CPU空閒時,Event Loop將它們放入運行堆棧執行。

MicroTask Queue用於很是短的,須要異步運行的操做,考慮以下場景:想要在稍後完成一些任務但又但願是在執行下一個Event隊列以前;通常使用dart:async庫中的scheduleMicrotask方法來實現;

Event Queue(事件隊列)包含全部外部事件:

  • I/O,
  • 手勢,
  • 鼠標事件,
  • 繪圖事件,
  • 計時器,
  • isolate 之間的消息
  • Future

每次外部事件被觸發時,要執行的相應代碼都會被添加到 Event Queue 中,當MicroTask隊列中沒有任何內容時,Event Loop纔會從Event 隊列中取出第一項來處理;須要重點關注的是,Future也會被添加到 Event 隊列中

當main()方法執行完成後,event loop開始它的工做,

1,先從 microtask 隊列以先進先出的方式取出並執行完全部內容;

2,從event 隊列中取出並處理第一項;

3,重複上述兩個步驟直到兩個隊列都沒有任何內容可執行

綜上所述,能夠由以下簡化圖來表示:

flowchart: main() -> microtasks -> next event -> microtasks -> ...

Future:

Future 一般指的是異步運行的任務,它會在將來某個時間點完成,這裏的完成有兩層含義:成功或者失敗,成功時返回任務執行的結果(注意:這裏的結果指的是 Future< T> 返回範型T的對象),失敗時返回錯誤;

當實例化一個 Future 的時候:

  • 該 Future 的一個實例被建立並記錄在由 Dart 管理的內部數組中;
  • 須要由此 Future 執行的代碼直接被推送到 Event 隊列中;
  • Future 實例返回一個未完成狀態的 Future 對象;
  • 若是 Future 語句以後還有其它的話(不是 Future 包含着的代碼),則繼續執行下一個同步代碼;
  • Future 完成後,then()方法及catchError()方法裏的代碼將會被執行;
import 'dart:async';

void main() {
  fallInLove(); //談戀愛;
  handleHouse(); //買房、入住(耗時、費用的操做)
  marry(); //結婚
  haveChild(); //生娃
}

///進行買房 [buyHouse]、入住[livingHouse]等操做
void handleHouse() {
  Future<House> house = buyHouse();
  house.then((_) {
    livingHouse();
  });
}

class House {}

Future<House> buyHouse() {
  Future<House> result = Future(() {
    print('buyHouse');
  });
  return result;
}

void livingHouse() {
  print('livingHouse');
}

void marry() {
  print('marry');
}

void haveChild() {
  print('haveChild');
}

void fallInLove() {
  print('fall in love');
}

咱們來分析上述代碼的執行順序:

  1. main()方法中開始執行同步代碼,首先執行fallInLove();
  2. 執行handleHouse()方法,將Future裏的(){print('buyHouse');}加入 Event 隊列;
  3. 執行marry()方法;
  4. 執行haveChild()方法;
  5. 執行到這裏的時候,main()方法已經執行完了,Event Loop開始處理兩個隊列中的元素,如前面的分析,這時候先查看MicroTask隊列有沒有須要處理的任務,沒有的話,就能夠取出Event隊列中的第一個任務來執行,在這個例子中,就是開始執行步驟2的(){print('buyHouse');}代碼塊;
  6. 當上述步驟完成以後,開始執行then()中的方法 livingHouse();;

因此代碼的執行結果應該以下所示:

fall in love
marry
haveChild
buyHouse
livingHouse

async/await:

​ 上面的 Future 章節,咱們主要使用了 Future 的 API 來達到異步操做的目的,Dart 還爲咱們提供了一對關鍵字 async/await 來達成此目的;有了這兩個關鍵字,咱們能夠像寫同步代碼那樣寫異步代碼,而且不用使用到 Future的 API (then());

使用 async/await 關鍵字有如下幾個須要注意的點:

  • async 關鍵字聲明的方法,須要返回 Future 對象:有可用值時類型爲 Future< T>;無可用值時爲 Future< void>;
  • await 關鍵字只能在 標記爲 async 的方法裏出現;
  • await 關鍵字後面跟着的表達式,一般是 Future 對象,若是不是,系統會自動封裝成Future 對象;
  • await 表達式會使表達式以後的語句暫停執行(async方法裏的執行),直到返回的 Future對象可用;
  • 對應於使用 Future API 中的 catchError()方法,能夠將await表達式包在 try/catch進行錯誤捕獲及後續處理;

以下:咱們只須要稍微改造handleHouse()方法:

  1. 在方法的 body 前加 async關鍵字標誌
  2. handleHouse()方法的返回類型改成 Future< void>
  3. 在須要耗時的操做方法前加 await關鍵字標誌
///進行買房 [buyHouse]、入住[livingHouse]等操做
Future<void> handleHouse() async {
  await buyHouse();
  livingHouse();
}

運行代碼後的輸出效果是與使用Future API 一致的;

總結

本文主要涉及到的概念有:isolate,event loop,future,async/await,理解了這些內容,可讓咱們更好地寫出、閱讀異步編程的相關代碼;

參考連接

參考連接1:https://dart.dev/tutorials/language/futures

參考連接2:https://dart.dev/guides/language/language-tour#asynchrony-support

參考連接3:https://dart.dev/articles/archive/event-loop#event-queue-new-future

相關文章
相關標籤/搜索