Flutter中的異步其實就是用的Dart里面的Future,then函數,回調catchError這些東西。下面舉例詳細解答一下使用過程和遇到的一些問題,讓大家更好的明白異步流程。
本文首發(fā)在公眾號Flutter那些事,歡迎大家多多關注。
Flutter系列博文鏈接 ↓:
工具安裝:
Flutter基礎篇:
- 谷歌Flutter1.0正式版發(fā)布
- Flutter基礎篇(1)-- 跨平臺開發(fā)框架和工具集錦
- Flutter基礎篇(2)-- 老司機用一篇博客帶你快速熟悉Dart語法
- Flutter基礎篇(3)-- Flutter基礎全面詳解
- Flutter基礎篇(4)-- Flutter填坑全面總結
- Flutter基礎篇(5)-- Flutter代碼模板,解放雙手,提高開發(fā)效率必備
- Flutter基礎篇(6)-- 水平和垂直布局詳解
- Flutter基礎篇(7)-- Flutter更新錯誤全面解決方案(圖文+視頻講解)
- Flutter基礎篇(8)-- Flutter for Web詳細介紹
- Flutter基礎篇(9)-- 手把手教你用Flutter實現(xiàn)Web頁面編寫
- Flutter1.9升級體驗總結(Flutter Web 1.9最新版本填坑指南)
Flutter進階篇:
- Flutter進階篇(1)-- 手把手帶你快速上手調試Flutter項目
- Flutter進階篇(2)-- Flutter路由詳解
- Flutter進階篇(3)-- Flutter 的手勢(GestureDetector)分析詳解
- Flutter進階篇(4)-- Flutter的Future異步詳解
- Flutter進階篇(5)-- 使用Flutter創(chuàng)建插件詳解并發(fā)布到Pub庫
- Flutter進階篇(6)-- PageStorageKey、PageStorageBucket和PageStorage使用詳解
- Flutter進階篇(7)-- Flutter路由輕量級框架FRouter
Dart語法系列博文鏈接 ↓:
Dart語法基礎篇:
Dart語法進階篇:
說明:本文中的所有函數的引用在main函數中:
main() async {
testFuture();
testFuture2();
testFutureCreate1();
testFutureCreate2();
testFutureCreate3();
testThen1();
testThen2();
testThen3();
testThen4();
testAll();
}
一、認識Future
1.創(chuàng)建Future
void testFuture(){
Future future = new Future(() => null);
future.then((_){
print("then");
}).then((){
print("whenComplete");
}).catchError((_){
print("catchError");
});
}
這里的執(zhí)行結果是:
then
whenComplete
Futue直接new就可以了。我這里沒有具體的返回數據,所以就用匿名函數代替了, Future future = new Future(() => null); 相當于 Future<Null> future = new Future(() => null); 泛型如果為null可以省略不寫,為了便于維護和管理,開發(fā)中建議加上泛型。
我們也可以直接在創(chuàng)建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:捕獲異?;蛘弋惒匠鲥e時的回調。
因為這里面的異步操作過程中沒有遇到什么錯誤,所以catchError回調不會調用。
在我們平時開發(fā)中我們是這樣用的,首先給我們的函數后面加上
async關鍵字,表示異步操作,然后函數返回值寫成Future,然后我們可以new一個Future,邏輯前面加上一個await關鍵字,然后可以使用future.then等操作。下面是一個示例操作,為了方便演示,這里的返回值的null。平時開發(fā)中如果請求網絡返回的是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");
});
}
二、創(chuàng)建多個Future的執(zhí)行步驟
1.我們這里創(chuàng)建3個Future,我們看看執(zhí)行過程:
void testFutureCreate1(){
Future f1 = new Future(() => null);
Future f2 = new Future(() => null);
Future f3 = new Future(() => null);
// 都是異步 空實現(xiàn) 順序進行
f1.then((_) => print("1"));
f2.then((_) => print("2"));
f3.then((_) => print("3"));
}
我們可以看到執(zhí)行結果是:
1
2
3
2.那么我們猜想是不是按照創(chuàng)建Future對象的先后順序依次執(zhí)行? 接下來我們打亂順序,再試試看!
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 和我們創(chuàng)建Future對象的先后順序完全一致。
2
1
3
3.接下來我們繼續(xù)猜想打亂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.【結論】: 創(chuàng)建多個Future,執(zhí)行順序和和創(chuàng)建Future的先后順序有關,如果只是單獨的調用then,沒有嵌套使用的話,和調用then的先后順序無關。
三、then函數嵌套使用的執(zhí)行步驟
當then回調函數里面還有then回調的時候,這時候的流程跟前面就不太一樣了,也是一個大坑,也是面試經常會被問到的一個知識點。
1.我們一開始就執(zhí)行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"));
}
我們可以看到執(zhí)行結果如下:
f1 -> f1
f2 -> f2
f2.then -> f1
f3 -> f3
2.接下來我們交換一下調用then的順序。我們發(fā)現(xiàn)結果是一樣的:
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
這里再次證明了上面我的猜想:執(zhí)行順序和和創(chuàng)建Future的先后順序有關,如果有多個then嵌套執(zhí)行,先執(zhí)行外面的then,然后執(zhí)行里面的then。
4. 接下來我們在then內部創(chuàng)建一個Future 看看執(zhí)行順序如何?
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"));
}
執(zhí)行結果如下,我們可以看到then內部創(chuàng)建的Future要等到then執(zhí)行完了,最后再去執(zhí)行的:
f1 -> f1
f2 -> f2
f3 -> f3
f3.then -> f1
f3.then -> new Future
5.【結論】: 首先執(zhí)行順序和創(chuàng)建Future的先后順序有關,如果遇到多個 then 嵌套,先執(zhí)行外面的 then,然后再執(zhí)行里面的then,如果then里面還有創(chuàng)建Future,要等到then執(zhí)行完畢,之后執(zhí)行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創(chuàng)建順序應該是
f3 f1 f2 f4 f5依次執(zhí)行。
我們看到then函數的調用情況,f3先調用,所以它應該先輸出。
緊接著是f2調用了,所以f2輸出。
緊接著f4調用了,f4應該輸出。
緊接著是f5調用then函數,這個比較特殊,它是then函數的嵌套使用,首先是一個打印語句,直接輸出,然后是new Future函數,它應該等then執(zhí)行完畢再去執(zhí)行,所以這里會去找下面的f1.then里面的邏輯,然后輸出f1,到此f5.then都執(zhí)行完畢,然后就是執(zhí)行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,并且后續(xù)程序走了剛出現(xiàn)了錯誤,則錯誤將直接轉發(fā)給返回的Future。
在大多數情況下,單獨使用catchError更可讀,可能使用test參數,而不是在單個then調用中同時處理value和error。
請注意,在添加監(jiān)聽器(listener)之前,future不會延遲報告錯誤。如果第一個then或catchError調用在future完成后發(fā)生error,那么error將報告為未處理的錯誤。