其實第一次接觸 DART 挺不習慣的一點就是,超多 Method 呼叫完畢後回傳的不是其值,而是回傳一個物件其名為 Future 的類別,這個名詞依照翻譯是未來對吧?不知道在程式語言中有沒有一個正名,所以還是先叫 Future比較好,看台灣有沒有偉大的作者出書來為其正名。
Future這個類別其實有點像是我們一般在寫的 CallBack,不同的是 CallBack竟然是回傳後再寫的,來看一個簡單的範例 :
import 'dart:io'; import 'dart:async'; import 'dart:convert'; void main() { // 建立一個 File 物件,這個 File 就是我自己本身了 File file = new File(Platform.script.toFilePath()); Future<String> finishedReading = file.readAsString(encoding: Encoding.getByName("UTF-8")); finishedReading.then((text){ print("Test 1 Text :\n$text"); } , onError: (e) { print("Test 1 Exception:\n$e"); }); // 建立一個 File 物件,這個 File 就是我自己本身加個 a , 根本不存在檔案 File file2 = new File(Platform.script.toFilePath() + "a"); Future<String> finishedReading2 = file2.readAsString(encoding: Encoding.getByName("UTF-8")); finishedReading2.then((text){ print("Test 2 Text :\n$text"); } , onError: (e) { print("Test 2 Exception:\n$e"); }); }
這個範例主要測試讀取自己本身的內容及讀取一個不存在的檔案,主要是以 File 這個物件呼叫 readAsString 欲讀取內容,而回傳的值會是一個 Future。而執行結果如下 :
Test 2 Exception: FileSystemException: Cannot open file, path = C:\Users\Pigo\workspace\learn\bin\learn.darta (OS Error: 系統找不到指定的檔案。 , errno = 2) Test 1 Text : import 'dart:io'; import 'dart:async'; import 'dart:convert'; void main() { // 建立一個 File 物件,這個 File 就是我自己本身了 File file = new File(Platform.script.toFilePath()); Future<String> finishedReading = file.readAsString(encoding: Encoding.getByName("UTF-8")); finishedReading.then((text){ print("Test 1 Text :\n$text"); } , onError: (e) { print("Test 1 Exception:\n$e"); }); // 建立一個 File 物件,這個 File 就是我自己本身加個 a , 根本不存在檔案 File file2 = new File(Platform.script.toFilePath() + "a"); Future<String> finishedReading2 = file2.readAsString(encoding: Encoding.getByName("UTF-8")); finishedReading2.then((text){ print("Test 2 Text :\n$text"); } , onError: (e) { print("Test 2 Exception:\n$e"); }); }
這個執行結果有個弔詭的地方就是,第二段讀取錯誤的檔案發生錯誤竟然優先被 print 出來了,原來啊~ 這真的是非同步的事件驅動啊,但這種非同步事件驅動難道只有 DART 才有嗎?其實大部分程式語言都可以做得到,看看 nodeJS :
fs.readFile('/yourpath/yourfilename', function (err, data) {
if (err) throw err;
console.log(data);
});
那為何 DART 要多搞一個 Future 來處理非同步這事呢 ?
這時候我們就要去看看 Future 的官方介紹了 https://www.dartlang.org/articles/using-future-based-apis/
內文中有提到一個點是我覺得比較難以處理的,用 Future 來處理的話應該比較簡單,就是當有一件事情一定要等到所有事情都做完之後才能繼續的時候,這其實不難理解,假如我今天想要吃完炒飯後才去洗澡,晚飯的材料有蛋和油和飯,我叫三個人去買,這三個人買回來的時間不一定,我一定要等到三個人買的材料齊全了才可以做炒飯。因此"等待"其他事件完成的這個功能是不是很重要?要不然我油先放了,蛋還要等10分鐘,那鍋子早就燒壞了是吧。因此 Future 還可以幹這事情 :
Future.wait([expensiveA(), expensiveB(), expensiveC()]) .then((List responses) => chooseBestResponse(responses)) .catchError((e) => handleError(e));
這段意思很簡單,他可以等待很多個 Future 完成之後再繼續做該做的事情,以前如果用其他語言要實現,可能要另外宣告變數來記錄那些事情完成了沒,可能還要用到 Thread ,而DART這樣的語法結構還挺直覺的。
如果換成 nodeJS,使用 async 的方式可以達到同樣的效果,如下列連結的提問
http://stackoverflow.com/questions/5172244/idiomatic-way-to-wait-for-multiple-callbacks-in-node-js
兩相比較,效果是一樣的,直覺性就看個人去慢慢感受了。
那有些人可能有疑問,那如果我就是想一項項自己來,也就是做同步式的應用怎麼辦呢?
其實在 DART 中每一個非同步的呼叫應該都會搭配一個同步的呼叫可以回傳值,如 File 這個類別讀取字串的同步呼叫方式是 readAsStringSync() ,而呼叫 readAsStringSync 可能會產生 FileSystemException 的例外,因此仍是有傳統的語法可以用的。只不過大部分在 DART 的命名方式中,回傳 Future 是預設的,若要使用同步的調用,後面加個 Sync 就對了。