flutter中的異步機制Future


餓補一下Flutter中Http請求的異步操作。

Dart是一個單線程語言,可以理解成物理線路中的串聯,當其遇到有延遲的運算(比如IO操作、延時執行)時,線程中按順序執行的運算就會阻塞,用戶就會感覺到卡頓,於是通常用異步處理來解決這個問題。

Dart異步編程有兩種方式:Future和Stream

Future相當於40米大砍刀,Stream相當於一捆40米大砍刀。dart提供了關鍵字async(異步)和await(延遲執行),相當於普通的便捷的小匕首,而小匕首是我們平時經常用到的。

當遇到有需要延遲的運算(async)時,將其放入到延遲運算的隊列(await)中去,把不需要延遲運算的部分先執行掉,最后再來處理延遲運算的部分。

1、async和await

async await 這兩個關鍵字是dart語言的特性,能讓你寫出看起來像是“同步”的“異步”代碼,先看一個方法案例:

  /*HTTP的get請求返回值為Future<String>類型,即其返回值未來是一個String類型的值*/
  /*async關鍵字聲明該函數內部有代碼需要延遲執行*/
  getData() async {    
    /*await關鍵字聲明運算為延遲執行,然后return運算結果*/
    return await http.get(Uri.encodeFull(url), headers: {"Accept""application/json"}); 
  }

然后我們嘗試調用這個方法,並獲取返回值。

String data = getDate();

然后控制台報錯了….

為什么呢?因為data是String類型,而函數getData()是一個異步操作函數,其返回值是一個await延遲執行的結果。在Dart中,有await標記的運算,其結果值都是一個Future對象,Future不是String類型,所以就報錯了。

總結一下:

在請求方法中直接 return await .. .的時候,實際上返回的是一個延遲計算的Future對象。

還有兩點需要注意:

  • await關鍵字必須在async函數內部使用
  • 調用async函數必須使用await關鍵字

后面兩點怎么講?

  1. 要想 return await … ,那么方法首先是 async 的,如上方方法。
  2. 使用 async 標注的方法,必須要用 await 接收返回值,比如上方方法在接收返回值時,需要加入 await ,var data = await getData();

2、什么是Future

Future表示一件“將來”會發生的事情,將來可以從Future中取到一個值。當一個方法返回一個Future的事情,發生兩件事情:

  • 這個方法將某件事情排隊,返回一個未完成的Future
  • 當這件事情完畢之后,Future的狀態會變成已完成,這個時候就可以取到這件事情的返回值了。

要取到這個“返回值”,有兩種方式:

  • 使用async配合await
  • 使用Future提供的api

我們看這兩種實現方式的案例:

2.1、使用async配合await

先看個案例,等待3秒后返回‘我是用戶’:

/*模擬異步加載用戶信息*/
Future _getUserInfo() async{
  await new Future.delayed(new Duration(milliseconds: 3000));
  return "我是用戶";
}

/*加載用戶信息,順便打印時間看看順序*/
Future _loadUserInfo() async{
  print("_loadUserInfo:${new DateTime.now()}");
  print(await _getUserInfo());
  print("_loadUserInfo:${new DateTime.now()}");
}

我們在initState中調用該方法:

@override
void initState(){
    print("initState:${new DateTime.now()}");
    _loadUserInfo();
    print("initState:${new DateTime.now()}");

    super.initState();
}

打印結果如下:

I/flutter ( 1802): initState:2019-06-20 09:46:40.097339
I/flutter ( 1802): _loadUserInfo:2019-06-20 09:46:40.103542
I/flutter ( 1802): Instance of 'Future<dynamic>'
I/flutter ( 1802): initState:2019-06-20 09:46:40.108510
I/flutter ( 1802): 我是用戶
I/flutter ( 1802): _loadUserInfo:2019-06-20 09:46:43.117136

what?

很明顯,打印結果並沒有按照串聯的方式依次打印。

flutter中會改造帶asyc關鍵字的方法,讓這個方法脫離主流程,變成“后面一點”執行(通過scheduleMicrotask),所以可以讓我們的程序“看起來”是順序執行的。

2.2、Future api

我們修改一下 loadUserInfo() 方法:

/*加載用戶信息,順便打印時間看看順序*/
  Future _loadUserInfo() async{
    print("_loadUserInfo:${new DateTime.now()}");
    _getUserInfo().then((info){
      print(info);
    });
    print("_loadUserInfo:${new DateTime.now()}");
  }

再次運行輸出一下:

I/flutter ( 1802): initState:2019-06-20 09:50:32.488765
I/flutter ( 1802): _loadUserInfo:2019-06-20 09:50:32.494751
I/flutter ( 1802): _loadUserInfo:2019-06-20 09:50:32.499725
I/flutter ( 1802): Instance of 'Future<dynamic>'
I/flutter ( 1802): initState:2019-06-20 09:50:32.499970
I/flutter ( 1802): 我是用戶

兩次輸出是有不同的,主要不同在於第二個 loadUserInfo 的日志打印,與‘我是用戶’的輸出順序,為什么有差異?

await會阻塞流程,等待緊跟着的的Future執行完畢之后,再執行下一條語句,而如果用了Future.then這個api,那么就不會等待,直接執行下面的語句,等Future執行完了,再調用then這個方法。

3、總結

在請求方法中直接 return await .. .的時候,實際上返回的是一個延遲計算的Future對象。

有兩點需要注意:

  • await關鍵字必須在async函數內部使用
  • 調用async函數必須使用await關鍵字

Flutter 中有兩種實現異步編程的方式:Future api、 async await

日常開發中常用的是 async await Future 搭配。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM