在寫 Dart 的過程中,一定會碰到許多需要非同步的情境,例如:打 Web Api 從 Server 端讀寫資料、存取 local storage、sqlite...等等。Future 讓非同步操作用起來很容易,但若不清楚其執行邏輯,有些時候難免會造成一些 bug,而非同步的 bug 又比較難以除錯。所以今天就來用個簡單的例子,來聊聊 Future 的執行邏輯。
void main() async {
print("1");
await loadData();
print("4");
}
Future loadData() async {
print("2");
await Future.delayed(Duration(seconds:1), () => "data");
print("3");
} 上面這段代碼執行結果是
1
2
3
4 因為每一段非同步都用了await來等待,會等到結果回傳了才會往下一步前進。執行過程如下:
- 執行 print("1") 並印出 1
- 進入 loadData() 方法中
- 執行 print("2") 並印出 2
- 執行 Future.delayed() 並回傳 Future
- await 接到 Future.delayed 回傳的 Future 後,發現 Future 尚未執行完成,loadData() 也從 await 處回傳 Future,程式控制權從 loadData() 回到 main() 中
- 同理,main 方法中的 await 發現 loadData() 回傳的 Future 尚未執行完成,所以 main() 也從 await 處回傳 Future,程式控制權也從 main() 回到呼叫 main() 的 framework 中
- Future.delayed() 執行完成,並從 loadData() 中的 await 處繼續往下執行
- 執行 print("3") 並印出 3
- loadData() 執行完成,並回傳完成執行的 Future 到 main() 中
- 回到 main() 後,從 await 處往下執行
- 執行 print("4") 並印出 4
移除 main 中的 await
把呼叫 loadData() 時的 await拿掉以後並在執行一次
void main() async {
print("1");
loadData();
print("4");
// 讓程式不要太快結束,避免來不及印出 3
await Future.delayed(Duration(seconds: 5), () => {});
}
Future loadData() async {
print("2");
await Future.delayed(Duration(seconds:1), () => "data");
print("3");
} 執行結果變成
1
2
4
3 與第一個例子不同的是,當 loadData() 回傳未完成的 Future 後,main() 並沒有用 await 等待,所以程式就繼續往下執行,並印出 4。過了一秒之後,Future.delayed 完成並從 loadData() 的 await 處往下執行,並印出 3。
移除 loadData 中的 await
我們稍微修改一下例子,讓 main() 同樣 await loadData(),但是 loadData() 不 await Future.delayed()
void main() async {
print("1");
await loadData();
print("4");
}
Future loadData() async {
print("2");
Future.delayed(Duration(seconds:1), () => "data");
print("3");
} 執行結果回到
1
2
3
4 與第一個例子不同的是,當 loadData() 執行到 Future.delayed() 時,此處沒有使用 await 來中斷執行,而是繼續往下執行並印出 3。當loadData() 執行完 print("3") 回傳 Future 到 main() 中,此時 main() 中的 await 發現 Future 已經執行完成,所以也就已同步的方式往下執行並印出 4。
執行 await 後的工作
如同之前提到的 Dart 非同步會使用 queue 來安排工作,從 await 處往下執行的這項工作也同樣會被排進 queue 中,也就是說在當前工作未完成之前,即使 await 處的工作已經完成,也無法繼續往下執行,讓我們來看看另外一個例子
void main() async {
print("1");
loadData();
sleep(Duration(seconds: 2));
print("4");
// 讓程式不要太快結束,避免來不及印出 3
await Future.delayed(Duration(seconds: 5), () => {});
}
Future loadData() async {
print("2");
await Future.delayed(Duration(seconds: 1), () => "data");
print("3");
} 執行結果是
1
2
4
3 執行完 main() 需要兩秒,而Future.delayed() 的工作只需要一秒,但是結果還是先印出4,再印出 3,因為一次只能有一項工作在執行,此時已經被 main() 方法佔住了,所以即便 Future.delayed() 已經執行完成,它也只能乖乖在 queue 中等待 main() 把 sleep() 和 print("4") 執行完,然後才輪到 print("3")。
小結
看了上面幾個例子之後,我們可以歸納一些結論
- 沒有 await 的 async 方法等於同步方法
- 當程式執行到 await 時,是否往下執行取決於當下 Future 的狀態
- 非同步的 Future 執行結束後會排入 queue 中等待執行