Dart 無所不在的未來事件 => Future

其實第一次接觸 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:&nbsp; 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:&nbsp; 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 就對了。

發表迴響