最近用Flutter寫了個簡單的「爬蟲」的東西,就是要不斷地請求某些接口,而後讀取數據就能夠了。以前只是會簡單地await和async來試用Future,後面發現只會簡單的這種方式不行,因而去學習了Future的其餘用法,仍是有點收穫的,把Future的用法都學了一下,這裏作個筆記。java
哈哈,別問我爲何沒用python去搞。。。恰好用的電腦沒裝環境而且python的API沒那麼熟,就有了個想法,反正都能實現,就用Flutter玩一下吧,可能後面還能夠搞個可視化界面玩玩。python
推薦文章:Dart與消息循環機制bash
這裏簡單說下Dart的事件循環,更加具體的話,能夠看推薦的這篇文章。網絡
相似Android的Handler/Looper機制,Dart也有相應的消息循環機制。dom
在寫程序的過程當中,確定會有一部分比較耗時代碼是須要異步執行的。好比網絡操做,咱們須要異步去請求數據,而且還須要處理請求成功和請求失敗的兩種狀況。異步
在Flutter中,使用Future來執行耗時操做,表示在將來會返回某個值,並可使用then()方法和catchError()來註冊callback來監聽Future的處理結果。async
以下面所示,源碼中對Future的解釋,以及簡單的一個例子。then()方法和catchError()返回的其實也是一個Future類型對象,因此能夠寫成鏈式調用的形式。函數
An object representing a delayed computation.
Future<int> future = getFuture();
future.then((value) => handleValue(value))
.catchError((error) => handleError(error));
複製代碼
一個Future對象會有如下兩種狀態oop
注意:首先Future是個泛型類,能夠指定類型。若是沒有指定相應類型的話,默認返回是Future類型的。學習
Future createFuture()async{
return 1;
}
var future=createFuture();
print(future.runtimeType);。//輸出結果爲Future<dynamic>
複製代碼
Future經常使用的建立有如下幾種方式。
Future的構造方法,建立一個基本的Future
var future = Future(() {
print("哈哈哈");
});
print("111");
//111
//哈哈哈
複製代碼
你會發現,結果是打印「哈哈哈」字符串是在後面才輸出的,緣由是默認future是異步執行的。若是改爲下面的代碼,在future前面加上await關鍵字,則會先打印「哈哈哈」字符串。await會等待直到future執行結束後,才繼續執行後面的代碼。
這個跟上面的事件循環是有關係的,轉發大佬的解釋:main方法中的普通代碼都是同步執行的,因此確定是main打印先所有打印出來,等main方法結束後會開始檢查microtask中是否有任務,如有則執行,執行完繼續檢查microtask,直到microtask列隊爲空。因此接着打印的應該是microtask的打印。最後會去執行event隊列。
var future = await Future(() {
print("哈哈哈");
});
print("111");
//哈哈哈
//111
複製代碼
建立一個返回指定value值的Future
var future=Future.value(1);
print(future);
//Instance of 'Future<int>'
複製代碼
建立一個延遲執行的future。 例以下面的例子,利用Future延遲兩秒後能夠打印出字符串。
var futureDelayed = Future.delayed(Duration(seconds: 2), () {
print("Future.delayed");
return 2;
});
複製代碼
根據某個集合,建立一系列的Future,而且會按順序執行這些Future。 好比下面的例子,根據{1,2,3}建立3個延遲對應秒數的Future。執行結果爲1秒後打印1,再過2秒打印2,再過3秒打印3,總時間爲6秒。
Future.forEach({1,2,3}, (num){
return Future.delayed(Duration(seconds: num),(){print(num);});
});
複製代碼
用來等待多個future完成,並收集它們的結果。那這樣的結果就有兩種狀況了:
好比下面的例子,也是建立3個延遲對應秒數的Future。結果是總時間過了3秒後,才輸出[1, 2, 3]的結果。能夠與上面的例子對比一下,一個是順序執行多個Future,一個是異步執行多個Future。
var future1 = new Future.delayed(new Duration(seconds: 1), () => 1);
var future2 =
new Future.delayed(new Duration(seconds: 2), () => 2);
var future3 = new Future.delayed(new Duration(seconds: 3), () => 3);
Future.wait({future1,future2,future3}).then(print).catchError(print);
//運行結果: [1, 2, 3]
複製代碼
將future2和future3改成有error拋出,則future.wait的結果是這個future2的error值。
var future1 = new Future.delayed(new Duration(seconds: 1), () => 1);
var future2 =new Future.delayed(new Duration(seconds: 2), () => throw 「throw error2"); var future3 = new Future.delayed(new Duration(seconds: 3), () => throw 「throw error3");
Future.wait({future1,future2,future3}).then(print).catchError(print);
//運行結果: throw error2
複製代碼
返回的是第一個執行完成的future的結果,不會管這個結果是正確的仍是error的。(感受這個的使用場景沒幾個的樣子,想不到有什麼場景要用到這個。)
例以下面的例子,使用Future.delayed()延遲建立了三個不一樣的Future,第一個完成返回的是延遲1秒的那個future的結果。
Future
.any([1, 2, 5].map(
(delay) => new Future.delayed(new Duration(seconds: delay), () => delay)))
.then(print)
.catchError(print);
複製代碼
相似java中doWhile的用法,重複性地執行某一個動做,直到返回false或者Future,退出循環。
使用場景:適用於一些須要遞歸操做的場景。
好比我就是要爬某個平臺的商品分類的數據,那每一個分類下面又有相應的子分類。那我就得拿對應父級分類的catId去請求接口,拿到這個分類下面的全部子分類。一樣的,對全部的子分類,也要進行同樣的操做,遞歸直到這個分類是一個葉子節點,也就是該節點下面沒有子分類(有個leaf爲true的字段)。哈哈,代碼我就沒貼了,其實也就是一個遞歸操做,直到知足條件退出future。
例以下面的例子,生成一個隨機數進行等待,直到十秒以後,操做結束。
void futureDoWhile(){
var random = new Random();
var totalDelay = 0;
Future
.doWhile(() {
if (totalDelay > 10) {
print('total delay: $totalDelay seconds');
return false;
}
var delay = random.nextInt(5) + 1;
totalDelay += delay;
return new Future.delayed(new Duration(seconds: delay), () {
print('waited $delay seconds');
return true;
});
})
.then(print)
.catchError(print);
}
//輸出結果:
I/flutter (11113): waited 5 seconds
I/flutter (11113): waited 1 seconds
I/flutter (11113): waited 3 seconds
I/flutter (11113): waited 2 seconds
I/flutter (11113): total delay: 12 seconds
I/flutter (11113): null
複製代碼
建立一個在microtask隊列運行的future。
在上面講過,microtask隊列的優先級是比event隊列高的,而通常future是在event隊列執行的,因此Future.microtask建立的future會優先於其餘future進行執行。
例以下面的代碼,
Future((){
print("Future event 1");
});
Future((){
print("Future event 2");
});
Future.microtask((){
print("microtask event");
});
//輸出結果
//microtask event
//Future event 1
//Future event 2
複製代碼
Flutter提供了下面三個方法,讓咱們來註冊回調,來監聽處理Future的結果。
Future<R> then<R>(FutureOr<R> onValue(T value), {Function onError});
Future<T> catchError(Function onError, {bool test(Object error)});
Future<T> whenComplete(FutureOr action());
複製代碼
用於在Future完成的時候添加回調。
注意:then()的返回值也是一個Future對象。因此咱們可使用鏈式的方法去使用Future,將前一個Future的輸出結果做爲後一個Future的輸入,能夠寫成鏈式調用。
例以下面的代碼,將前面的future結果做爲後面Future的輸入。
Future.value(1).then((value) {
return Future.value(value + 2);
}).then((value) {
return Future.value(value + 3);
}).then(print);
//打印結果爲6
複製代碼
註冊一個回調,來處理有error的Future
new Future.error('boom!').catchError(print);
複製代碼
與then方法中的onError的區別:then方法裏面也有個onError的參數,也能夠用來處理錯誤的Future。
二者的區別,onError只能處理當前的錯誤Future,而不能處理其餘有錯誤的Future。catchError能夠捕獲到Future鏈中拋出的全部錯誤。
因此一般的作法是使用catchError來捕捉Future中的全部錯誤,不建議使用then方法中的onError方法。否則每一個future的then方法都要加上onError回調的話,就比較麻煩了,並且代碼看起來也是有點亂。
下面是兩個捕捉錯誤的例子。
在那個拋出錯誤的future的then方法裏添加onError回調的話,onError會優先被調用
Future.value(1).then((value) {
return Future.value(value + 2);
}).then((value) {
throw "error";
}).then(print, onError: (e) {
print("onError find a error");
}).catchError((e) {
print("catchError find a error");
});
//輸出結果爲onError find a error
複製代碼
使用catchError來監聽鏈式調用Future裏面拋出來的錯誤。
Future.value(1).then((value) {
throw "error";
}).then((value) {
return Future.value(3);
}).then(print).then((value) {
}).catchError((e) {
print("catchError find a error");
});
//輸出結果爲catchError find a error" 複製代碼
相似Java中的finally,Future.whenComplete老是在Future完成後調用,無論Future的結果是正確的仍是錯誤的。
注意:Future.whenComplete的返回值也是一個Future對象。
Future.delayed(Duration(seconds: 3),(){
print("哈哈");
}).whenComplete((){
print("complete");
});
//哈哈
//complete
複製代碼
哈哈,上面說了一些Future的用法,其實有些可能並不會很經常使用,知道有這個用法就好了。
有個問題,若是有多個Future連接在一塊兒的話,靠,代碼可能會變得難以閱讀。
因此,可使用async和await來使用Future,使代碼看起來像是同步的代碼,但實際上它們仍是異步執行的。
最經常使用的仍是async和await來使用Future。
注意:await只能在async函數出現。 async函數,返回值是一個Future對象,若是沒有返回Future對象,會自動將返回值包裝成Future對象。 捕捉錯誤,通常是使用try/catch機制來作異常處理。 await 一個future,能夠拿到Future的結果,直到拿到結果,才執行下一步的代碼。
好比下面的例子,建立一個延遲3秒並返回結果爲1的Future,使用await來獲取到future的值。
void main() async {
var future = new Future.delayed(new Duration(seconds: 3), () {
return 1;
});
var result = await future;
print(result + 1);
}
//輸出爲2
複製代碼
下面的代碼結構,跟java同樣的寫法,使用try-catch-finally來捕捉錯誤。
try {
var result1 = await Future.value(1);
print(result1);//輸出1
var result2 = await Future.value(2);
print(result2);//輸出2
} catch (e) {
//捕捉錯誤
} finally {
}
複製代碼
多學習多整理。靠,又懶了好長一段時間了。。。