What are Async and Isolates in Flutter?
這篇寫於 2022 年。工具和 API 變化很快,部分步驟可能已經不適用。
創造高品質的用戶體驗
Async 非同步 ➤ 並發(Concurrency)
Isolates 隔離 ➤ 並行(Parallelism)
Flutter 基本運作#
- 1 線程 (Thread)、1 處理器 (Processor)
- 主隔離(main isolate)上運行
- 主隔離運行時會處理很多事件,例如:點擊螢幕、I/O完成載入、重新繪製與更新,它們會被加入 Event Queue,接著一個接一個在 Event Loop 處理,先到先出
- 為了平滑渲染,Flutter 每秒60次向 Event Queue 添加重繪的事件,維持順暢
Async#
Async is the ability to wait for other things without blocking.
- 搭配
await操作,函式結束後,才會接著處理後方程式碼 - 使用時不會自動生成線程,Dart會根據需求自動在不同程式區塊裡跳轉
- 在
Dart VM Thread上運行,當 任務完成後會向主隔離的Event Queue添加事件並標示任務完成,有點類似 Callback - APP裡其他程式碼一樣在運行,在
await進行處理時處理器不需要停止或等待 - 符合大多數的開發情境,但不適合有複雜處理的同步任務,例如:解析大量資料,這時APP其他部分都不會運行,可能會造成卡頓,需要 Isolate 解決這個問題
舉例:
- Async 就像是在跟人聊天的時候,快速檢查手機訊息,短短0.5秒停頓,對方感受不出來
- 但如果在跟人聊天的時候,這時剛好有重要訊息需要確認,可能盯著訊息5秒以上,接著再回來這段對話,對方應該會覺得尷尬或不舒服。這個情況會需要 Isolate 處理
Isolates#
Isolate is the ability to run things in parallel. It can offload heavy computations in the app to a background worker.
- 可稱
worker isolate、background isolate、background worker - 每個 Isolate 都有自己的記憶體(不共享),還有運行 Event Loop 的線程
Isolate與Thread的不同之處在於有自己的內存- 使用
Isolate時不同程式碼在不同線程(處理器)裡全速運作,互不影響 - 背景處理任務,同時保持APP響應與流暢動畫
- 適合複雜的計算,需要大量處理器效能的場景
- 兩幀內無法完成的任務需使用
Isolate解決,確保主線程每秒產生60幀,以獲得流暢的體驗 - 當有多個
Isolate同時產生時,無法確保每次都以相同的順序運行 Isolates之間的訊息傳輸通常涉及資料複製,因此可能速度慢並且隨著訊息的大小線性增加(O(n))
舉例:
- 在跟人聊天的時候,手在抓癢,同時做兩件事卻不干擾
使用情境 1#
await compute(_printName, 'Yii');
short-livedbackground tasks ,短時間的運算- 使用 Flutter 內建的
compute()函式,產生一個Isolate - 第一個參數:需要隔離處理的 function
- 第二個參數:function 的參數
使用情境 2#
long-livedbackground tasks,長時間執行複雜的運算與處理- 自定義隔離,使用
spawn產生,並透過SendPort和ReceivePort進行 main isolate 與 worker isolate 的溝通
spawn():建立隔離
Isolate.spawn(_entryPoint, _receivePort.sendPort);
- 第一個參數:
Isolate執行的函式 - 第二個參數:
ReceivePort的SendPort,給 worker isolate 跟 main isolate 溝通使用。當有多個函式參數需要使用時,可以透過List<dynamic> args,裡面再透過 args[0]、args[1] 取得資料。也可以自定義一個協議Model類,裡面包含主要的SendPort跟其他的資料欄位,就不用擔心只能傳一個參數的問題
ReceivePort:訊息接收通道
- 本身透過
Stream實作,可以持續監聽 - 第一個訊息通常是其他 Isolate 的
SendPort,可以使用它發送訊息給 worker isolate first屬性,取得發送過來的訊息,為Future<dynamic>。一般使用 first 後 stream 就會被關閉,如果需要持續接收訊息,需要將 ReceivePort 轉成廣播流,使用asBroadcastStream()sendPort屬性,提供給其他 isolate 發送訊息用
StreamQueue:接收訊息的佇列
StreamQueue _streamQueue = StreamQueue(_receivePort);
// Get new message from another isolate
await streamQueue.next;
// Stop receiving messages
await streamQueue.cancel();
- 類似 ReceivePort 的 broadcastStream
- 參數:
ReceivePort,給 worker isolate 使用 next(),負責取得其他 Isolates 透過SendPort傳送的訊息cancel(),停止訊息的監聽
SendPort:訊息發送通道
sendPort.send('message');
下面是練習範例 ,情境為從許多檔案取得Json內容並解析 ↓
jsonFileNameList 記錄 3 個 Json檔案,待會要處理
主隔離的 getJsonDataFromFiles() 負責創建 Isolate 幫忙處理檔案解析
isolateParsingFile() 提供給工作隔離,這是它的工作內容
主程式 main() 呼叫 getJsonDataFromFiles(),並將每次解析到的 Json 資料印出來
範例#
參考#
- https://dart.dev/guides/language/concurrency#how-isolates-work
- https://blog.codemagic.io/understanding-flutter-isolates/
- https://www.tutorialspoint.com/dart_programming/dart_programming_concurrency.htm
- https://www.youtube.com/watch?v=5AxWC49ZMzs&ab_channel=Flutter
- https://www.youtube.com/watch?v=yUMjt0AxVHU&ab_channel=Flutter
其他文章#
關於我#
- Github:https://github.com/chyiiiiiiiiiiii
- Linkedin:https://www.linkedin.com/in/yiichenhi/
- Youtube:Yii
- Youtube:一起饅頭(美食頻道)
- Instagram:yiichenhi
- Email:ab20803@gmail.com
贊助#
謝謝你!這是一個很長的文章而且你花費時間看完,真的非常感謝。
還有一件事, 如果覺得文章不錯可以贊助我,讓我有更多的動力和熱情分享 學習筆記和生活!請我喝一杯咖啡吧~
最後#
希望有幫助到你/妳,歡迎追蹤我,方便瀏覽最新的文章,也請別忘記給我拍手哦 (最多可以達到50次)!
本文原刊登於 Medium。