持續發燒,試試Dart語言的非同步操作,效率提升500%

ciscopuke發表於2021-09-09

前言

昨天發了篇文章《Dart開發服務端,我是不是發燒(騷)了》,承蒙小編看得起上了首頁。

今天持續發燒,再來寫寫如何使用 Dart 語言的非同步操作。說起非同步操作,玩 NodeJS 的同學會心一笑,這就是我們的看家本領啊。玩 PHP, JAVA 的同學也就看看,表示我們光看不說話。

程式碼演示之前,我們先假設一個場景。假設我有一些漂亮妹妹,我別出心裁的想發電子郵件給他們,表達愛心。在這裡個過程中,程式碼需要做的事情:

  1. 接收請求
  2. 儲存我的郵件內容到資料庫
  3. 還需要把郵件內容傳送到她們的郵箱。
  4. 返回結果

這個過程中,我關心的是發一封郵件需要多長時間,因為我妹妹太多了,一封郵件的時間太長的話,我就沒辦法照顧到其他人了。那上面4個步驟裡,哪些步驟會耗時呢?

很顯然,1 和 4 基本絕對不耗時,2 需要點時間,但是時間很短,3 需要的時間最長,因為涉及到網路傳輸,不可控因素太多。

同步程式碼是什麼樣的

我們先用同步程式碼的方式來模擬上面的流程。

假設儲存資訊到資料庫需要 1 秒,傳送郵件到對方郵箱需要 5 秒,總體應該是 6 點多。

import 'dart:io';

main() {
  acceptRequest(); //接受請求
  saveToDb();      //儲存到資料庫,不太耗時, 假設需要1秒
  sendLetter();    //傳送郵件到對方郵箱,非常耗時, 假設需要5秒
  returnRes();     //返回結果
}

void acceptRequest() {
  print(DateTime.now().toString() + ' 接受請求');
}

void saveToDb() {
  sleep(Duration(seconds: 1));
  print(DateTime.now().toString() + ' 儲存資料庫成功');
}

void sendLetter() {
  sleep(Duration(seconds: 5));
  print(DateTime.now().toString() + ' 傳送郵件成功');
}

void returnRes() {
  print(DateTime.now().toString() + ' 返回結果');
}

執行它,得到列印的結果

2021-06-29 16:40:44.993785 接受請求
2021-06-29 16:40:46.000240 儲存資料庫成功
2021-06-29 16:40:51.002400 傳送郵件成功
2021-06-29 16:40:51.002400 返回結果

簡單計算一下,從接受請求到返回結果,總共耗時 6 秒左右,符合預期。

非同步程式碼又是什麼樣子

剛才說了,我有好多漂亮妹妹,則一封郵件都要那麼長時間,那麼多妹妹得多長時間啊,能不能快點呢?

當然可以了,程式碼如下:

main() async {
  acceptRequest(); //接受請求
  await saveToDb(); //儲存到資料庫,不太耗時, 需要1秒
  sendLetter(); //傳送郵件到對方郵箱,非常耗時, 需要5秒
  returnRes(); //返回結果
}

void acceptRequest() {
  print(DateTime.now().toString() + ' 接受請求');
}

void saveToDb() async {
  await Future.delayed(Duration(seconds: 1));
  print(DateTime.now().toString() + ' 儲存資料庫成功');
}

void sendLetter() async {
  await Future.delayed(Duration(seconds: 5));
  print(DateTime.now().toString() + ' 傳送郵件成功');
}

void returnRes() {
  print(DateTime.now().toString() + ' 返回結果');
}

執行它,得到列印結果

2021-06-29 16:47:46.931323 接受請求
2021-06-29 16:47:47.956545 儲存資料庫成功
2021-06-29 16:47:47.959539 返回結果
2021-06-29 16:47:52.960542 傳送郵件成功

這個結果,可需要注意看了。有兩點需要特別注意:

  1. 從接收請求到返回結果,總共消耗了1秒左右
  2. 傳送郵件成功,竟然出現在返回結果得後面,間隔5秒

為什麼是這樣? 實際上這就是 Dart語言非同步操作得魅力所在。

Dart預設情況下是按照程式碼順序來執行任務。

但是當執行到 sendLetter 得時候,發現它是一個 async 非同步的操作,並且暫時不用等待它,然後就直接跳過他,執行了後面 returnRes ,因此列印出了 返回結果

返回結果之後,如果是瀏覽器請求的話,那麼這個瀏覽器請求就直接結束了。但是事情並沒有結束,Dart繼續執行了剛剛跳過的 sendLetter, 所以最後列印出了 傳送郵件成功

整體下來,我這次發郵件,只用了 1 秒鐘,而之前是 6 秒啊,這個效率提升,足足有 500%

嗯嗯,真是太棒了,可以照顧到更多妹妹了。

await async 究竟是個啥

眼尖的同學估計看出來了,上面的程式碼中

main() async {
  acceptRequest(); //接受請求
  await saveToDb(); //儲存到資料庫,不太耗時, 需要1秒
  sendLetter(); //傳送郵件到對方郵箱,非常耗時, 需要5秒
  returnRes(); //返回結果
}

saveToDb 儲存資料庫 與 sendLetter 傳送油價都是耗時操作,為什麼 saveToDb 前面加了 await

這是因為, saveToDb 也是非同步操作,如果不加 await ,它就會像 sendLetter 傳送郵件一樣,先被跳過,瀏覽器返回結果後,才被執行。這樣會產生一個問題,如果寫入資料庫失敗了,但是你已經告訴使用者成功了,這不尷尬了嗎?

所以, saveToDb 前面加了 await, 告訴 Dart 這段程式碼雖然是非同步的,你要同步執行。

總結

當一個操作非常耗時的話,我們就可以將其設定成非同步 async,先給使用者返回資訊,再慢慢處理。

如果想把某非同步操作變為同步的話, 可以加關鍵字 await, 表示我願意等待這個非同步結果。

Dart 提供了非同步操作的機制,我們可以很方便的來使用他們。

NodeJS 的哭了,看家本領被人給偷了。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4369/viewspace-2797036/,如需轉載,請註明出處,否則將追究法律責任。

相關文章