Flutter中的異步其實就是用的Dart里面的Future,then函數,回調catchError這些東西。下面舉例詳細解答一下使用過程和遇到的一些問題,讓大家更好的明白異步流程。
本文首發在公眾號Flutter那些事
,歡迎大家多多關注。
Flutter系列博文鏈接 ↓:
工具安裝:
Flutter基礎篇:
- 谷歌Flutter1.0正式版發布
- Flutter基礎篇(1)-- 跨平臺開發框架和工具集錦
- Flutter基礎篇(2)-- 老司機用一篇博客帶你快速熟悉Dart語法
- Flutter基礎篇(3)-- Flutter基礎全面詳解
- Flutter基礎篇(4)-- Flutter填坑全面總結
- Flutter基礎篇(5)-- Flutter代碼模板,解放雙手,提高開發效率必備
- Flutter基礎篇(6)-- 水平和垂直布局詳解
- Flutter基礎篇(7)-- Flutter更新錯誤全面解決方案(圖文+視頻講解)
- Flutter基礎篇(8)-- Flutter for Web詳細介紹
- Flutter基礎篇(9)-- 手把手教你用Flutter實現Web頁面編寫
- Flutter1.9升級體驗總結(Flutter Web 1.9最新版本填坑指南)
Flutter進階篇:
Dart語法系列博文鏈接 ↓:
Dart語法基礎篇:
Dart語法進階篇:
說明:本文中的所有函數的引用在main
函數中:
main() async {
testFuture();
testFuture2();
testFutureCreate1();
testFutureCreate2();
testFutureCreate3();
testThen1();
testThen2();
testThen3();
testThen4();
testAll();
}
一、認識Future
1.創建Future
void testFuture(){
Future future = new Future(() => null);
future.then((_){
print("then");
}).then((){
print("whenComplete");
}).catchError((_){
print("catchError");
});
}
這里的執行結果是:
then
whenComplete
Futue直接new就可以了。我這里沒有具體的返回數據,所以就用匿名函數代替了, Future future = new Future(() => null); 相當于 Future<Null> future = new Future(() => null); 泛型如果為null可以省略不寫,為了便于維護和管理,開發中建議加上泛型。
我們也可以直接在創建Future的時候直接輸出一些內容,例如:
void testFuture2(){
Future f1 = new Future(() => print("1"));
Future f2 = new Future(() => print("2"));
Future f3 = new Future(() => print("3"));
}
輸出結果是:
1
2
3
2.Future相關回調函數
future里面有幾個函數:
then
:異步操作邏輯在這里寫。
whenComplete
:異步完成時的回調。
catchError
:捕獲異常或者異步出錯時的回調。
因為這里面的異步操作過程中沒有遇到什么錯誤,所以catchError回調不會調用。
在我們平時開發中我們是這樣用的,首先給我們的函數后面加上
async
關鍵字,表示異步操作,然后函數返回值寫成Future
,然后我們可以new一個Future
,邏輯前面加上一個await
關鍵字,然后可以使用future.then
等操作。下面是一個示例操作,為了方便演示,這里的返回值的null。平時開發中如果請求網絡返回的是json,我們可以把泛型寫成String;泛型也可能是實體類(entity/domain),不過要做json轉實體類相關操作。
Future asyncDemo() async{
Future<Null> future = new Future(() => null);
await future.then((_){
print("then");
}).then((){
print("whenComplete");
}).catchError((_){
print("catchError");
});
}
二、創建多個Future的執行步驟
1.我們這里創建3個Future,我們看看執行過程:
void testFutureCreate1(){
Future f1 = new Future(() => null);
Future f2 = new Future(() => null);
Future f3 = new Future(() => null);
// 都是異步 空實現 順序進行
f1.then((_) => print("1"));
f2.then((_) => print("2"));
f3.then((_) => print("3"));
}
我們可以看到執行結果是:
1
2
3
2.那么我們猜想是不是按照創建Future對象的先后順序依次執行? 接下來我們打亂順序,再試試看!
void testFutureCreate2(){
Future f2 = new Future(() => null);
Future f1 = new Future(() => null);
Future f3 = new Future(() => null);
f1.then((_) => print("1"));
f2.then((_) => print("2"));
f3.then((_) => print("3"));
}
我們可以看到輸出結果是: 2 1 3
和我們創建Future對象的先后順序完全一致。
2
1
3
3.接下來我們繼續猜想打亂then的調用先后順序試試看?會不會有影響?
void testFutureCreate3(){
Future f1 = new Future(() => null);
Future f2 = new Future(() => null);
Future f3 = new Future(() => null);
f3.then((_) => print("3"));
f1.then((_) => print("1"));
f2.then((_) => print("2"));
}
我們可以看到結果為1 2 3
,和我們調用then的先后順序無關。:
1
2
3
4.【結論】: 創建多個Future,執行順序和和創建Future的先后順序有關,如果只是單獨的調用then,沒有嵌套使用的話,和調用then的先后順序無關。
三、then函數嵌套使用的執行步驟
當then回調函數里面還有then回調的時候,這時候的流程跟前面就不太一樣了,也是一個大坑,也是面試經常會被問到的一個知識點。
1.我們一開始就執行f1的then回調,接著是f2的then回調里面,包含一個f1的then回調,最后是f3的then回調。示例如下:
void testThen1() {
Future f1 = new Future(() => null);
Future f2 = new Future(() => null);
Future f3 = new Future(() => null);
f1.then((_) => print("f1 -> f1"));
// f2 then 異步回調里面還有異步回調
f2.then((_) {
print("f2 -> f2");
f1.then((_) => print("f2.then -> f1"));
});
f3.then((_) => print("f3 -> f3"));
}
我們可以看到執行結果如下:
f1 -> f1
f2 -> f2
f2.then -> f1
f3 -> f3
2.接下來我們交換一下調用then的順序。我們發現結果是一樣的:
void testThen2() {
Future f1 = new Future(() => null);
Future f2 = new Future(() => null);
Future f3 = new Future(() => null);
f2.then((_) {
print("f2 -> f2");
f1.then((_) => print("f2.then -> f1"));
});
f1.then((_) => print("f1 -> f1"));
f3.then((_) => print("f3 -> f3"));
}
結果還是一樣的:
f1 -> f1
f2 -> f2
f2.then -> f1
f3 -> f3
3.接下來我們調整一下。
void testThen3() {
Future f1 = new Future(() => null);
Future f2 = new Future(() => null);
Future f3 = new Future(() => null);
f1.then((_) => print("f1 -> f1"));
f3.then((_) {
print("f3 -> f3");
f1.then((_) => print("f3.then -> f1"));
});
f2.then((_) => print("f2 -> f2"));
}
運行結果是:
f1 -> f1
f2 -> f2
f3 -> f3
f3.then -> f1
這里再次證明了上面我的猜想:執行順序和和創建Future的先后順序有關,如果有多個then嵌套執行,先執行外面的then,然后執行里面的then。
4. 接下來我們在then內部創建一個Future 看看執行順序如何?
void testThen4() {
Future f1 = new Future(() => null);
Future f2 = new Future(() => null);
Future f3 = new Future(() => null);
f1.then((_) => print("f1 -> f1"));
f3.then((_) {
print("f3 -> f3");
new Future(() => print("f3.then -> new Future"));
f1.then((_) => print("f3.then -> f1"));
});
f2.then((_) => print("f2 -> f2"));
}
執行結果如下,我們可以看到then內部創建的Future要等到then執行完了,最后再去執行的:
f1 -> f1
f2 -> f2
f3 -> f3
f3.then -> f1
f3.then -> new Future
5.【結論】: 首先執行順序和創建Future的先后順序有關,如果遇到多個 then 嵌套,先執行外面的 then
,然后再執行里面的then
,如果then
里面還有創建Future
,要等到then
執行完畢,之后執行Future
。
四、綜合示例
void testAll() {
Future f3 = new Future(() => null);
Future f1 = new Future(() => null);
Future f2 = new Future(() => null);
Future f4 = new Future(() => null);
Future f5 = new Future(() => null);
f3.then((_) => print("f3.then -> f3"));
f2.then((_) => print("f2.then -> f2"));
f4.then((_) => print("f4.then -> f4"));
f5.then((_) {
print("f5.then -> f5");
new Future(() => print("f5.then -> new Future"));
f1.then((_) {
print("f1.then -> f1");
});
});
}
根據上文總結的特點,我們可以不用運行也能推斷出輸出結果:
首先按照Future創建順序應該是
f3 f1 f2 f4 f5
依次執行。
我們看到then函數的調用情況,f3先調用,所以它應該先輸出。
緊接著是f2調用了,所以f2輸出。
緊接著f4調用了,f4應該輸出。
緊接著是f5調用then
函數,這個比較特殊,它是then
函數的嵌套使用,首先是一個打印語句,直接輸出,然后是new Future函數,它應該等then執行完畢再去執行,所以這里會去找下面的f1.then
里面的邏輯,然后輸出f1,到此f5.then
都執行完畢,然后就是執行new Future
里面的邏輯(如果沒有內部嵌套then
的話,它就會直接輸出。)
為了驗證我們的猜想,我們打印一下輸出結果,果然我們的證明是正確的。
f3.then -> f3
f2.then -> f2
f4.then -> f4
f5.then -> f5
f1.then -> f1
f5.then -> new Future
五、我們來看看Future的源碼說明文檔
我們重點看看then函數的文檔說明:
then
注冊在 Future
完成時調用的回調。
當這個 Future
用一個 value
完成時,將使用該值調用onValue
回調。
如果 Future
已經完成,則不會立即調用回調,而是將在稍后的microtask(微任務)
中調度。
如果回調返回Future
,那么then
返回的future
將與callback
返回的future
結果相同。
onError
回調必須接受一個參數或兩個參數,后者是[StackTrace]。
如果onError
接受兩個參數,則使用錯誤和堆棧跟蹤時調用它,否則僅使用錯誤對象時候調用它。
onError
回調必須返回一個可用于完成返回的future的值或future,因此它必須是可賦值給FutureOr <R>
的東西。
返回一個新的Future
,該Future
是通過調用onValue
(如果這個Future是通過一個value完成的)或'onError
(如果這個Future是通過一個error完成的)的結果完成的。
如果調用的回調拋出異常,返回的future
將使用拋出的錯誤和錯誤的堆棧跟蹤完成。在onError
的情況下,如果拋出的異常與onError
的錯誤參數“相同(identical)”,則視為重新拋出,并使用原始堆棧跟蹤替代
如果回調返回Future
,則then
返回的Future
將以與回調返回的Future
相同的結果完成。
如果未給出onError
,并且后續程序走了剛出現了錯誤,則錯誤將直接轉發給返回的Future
。
在大多數情況下,單獨使用catchError
更可讀,可能使用test
參數,而不是在單個then
調用中同時處理value
和error
。
請注意,在添加監聽器(listener)之前,future
不會延遲報告錯誤。如果第一個then
或catchError
調用在future
完成后發生error
,那么error
將報告為未處理的錯誤。