Dart中的Future使用

ityongzhen發表於2020-05-05

本文首發於個人部落格

前言

關於非同步,相信很多開發者都是經常用到的。不過,不同的語言有不同的處理方式

一般耗時操作的處理

針對如何處理耗時的操作,不同的語言有不同的處理方式。

  • 處理方式一: 多執行緒,比如JavaC++Objective C我們普遍的做法是開啟一個新的執行緒(Thread),在新的執行緒中完成這些非同步的操作,再通過執行緒間通訊的方式,將拿到的資料傳遞給主執行緒。

  • 處理方式二: 單執行緒+事件迴圈,比如JavaScriptDart都是基於單執行緒加事件迴圈來完成耗時操作的處理。

阻塞式呼叫和非阻塞式呼叫

  • 阻塞和非阻塞關注的是程式在等待呼叫結果(訊息,返回值)時的狀態。

  • 阻塞式呼叫: 呼叫結果返回之前,當前執行緒會被掛起,呼叫執行緒只有在得到呼叫結果之後才會繼續執行。

  • 非阻塞式呼叫: 呼叫執行之後,當前執行緒不會停止執行,只需要過一段時間來檢查一下有沒有結果返回即可。

例如你平時做飯的時候,把米放在電飯煲裡面煮,這時候你可以在旁邊坐著等著米飯做好,然後去做菜,這就是阻塞式呼叫。當然了。你也可以煮飯時候,準備菜,啤酒鴨,麻辣小龍蝦。。這就是非阻塞式呼叫。

Dart事件迴圈

什麼是事件迴圈

單執行緒模型中主要就是在維護著一個事件迴圈(Event Loop)。

事件迴圈是什麼呢?

  • 事實上事件迴圈並不複雜,它就是將需要處理的一系列事件(包括點選事件、IO事件、網路事件)放在一個事件佇列(Event Queue)中。

  • 不斷的從事件佇列(Event Queue)中取出事件,並執行其對應需要執行的程式碼塊,直到事件佇列清空位置。

  • 當我們有一些事件時,比如點選事件、IO事件、網路事件時,它們就會被加入到eventLoop中,當發現事件佇列不為空時發現,就會取出事件,並且執行。

具體使用

例子

我們先看一個例子:如下程式碼中,用sleep代替網路請求的耗時操作

import 'dart:io';

main(List<String> args) {
  
  print("start");
  String res = getData();
  print(res);
  print("end");

}

String getData(){
  sleep(Duration(seconds: 2));
  return "hello world";
}
複製程式碼

輸出結果為:

start

2秒鐘之後輸出

hello world

end

很顯然阻塞了後面的程式碼執行

使用Future

Future 表示一件“將來”會發生的事情,將來可以從Future中取到一個值。 有了Future之後,通過.then的回撥去獲取請求到的結果

import 'dart:io';

main(List<String> args) {
  
  print("start");
  var res = getData();
  res.then((value) {
    print(value);
  });
  print("end");

}

Future<String> getData(){

  return Future<String>(() {
     sleep(Duration(seconds: 2));
     return "hello world";
  });
 
}
複製程式碼

catchError

如果有異常的話,我們可以用catchError來捕獲

import 'dart:io';

main(List<String> args) {
  
  print("start");
  var res = getData();
  res.then((value) {
    print(value);
  }).catchError((error){// 捕獲出現異常時的情況
    print(error);

  });
  print("end");

}

Future<String> getData(){

  return Future<String>(() {
     sleep(Duration(seconds: 2));
    //  return "hello world";
    throw Exception("請求異常");

  });
 
}
複製程式碼

輸出為

start

end

Exception: 請求異常

Future的鏈式呼叫

import 'dart:io';

main(List<String> args) {
  
  print("start");
  var res = getData();
  res.then((value) {
    print(value);
    return "第一次呼叫完成";
  }).then((value2){

     print(value2);
    return "第二次呼叫完成";
  }).then((value3){

     print(value3);
  }).catchError((error){
    print(error);

  });
  print("end");

}

Future<String> getData(){

  return Future<String>(() {
     sleep(Duration(seconds: 2));
     return "hello world";
    // throw Exception("請求異常");
  });
 
}
複製程式碼

輸出

start

end

hello world

第一次呼叫完成

第二次呼叫完成

直接獲取一個完成的Future,該Future會直接呼叫then的回撥函式

import 'dart:io';

main(List<String> args) {
  
  print("start");
  
  Future.value("測試").then((value){
    print(value);
  });
  print("end");

}
複製程式碼

輸出為:

start

end

測試

我們可以看到,測試是最後才列印,這是因為Future中的then會作為新的任務會加入到事件佇列中(Event Queue),加入之後你需要排隊執行。

總結

  • 建立一個Future(可能是我們建立的,也可能是呼叫內部API或者第三方API獲取到的一個Future,總之你需要獲取到一個Future例項,Future通常會對一些非同步的操作進行封裝);
  • 通過.then(成功回撥函式)的方式來監聽Future內部執行完成時獲取到的結果;
  • 通過.catchError(失敗或異常回撥函式)的方式來監聽Future內部執行失敗或者出現異常時的錯誤資訊;
  • 可以鏈式呼叫
  • 直接獲取一個完成的Future,該Future會直接呼叫then的回撥函式

QQ交流群:592831498

個人部落格ityongzhen.github.io

相關文章