Flutter&Dart Callback轉同步
阿新 • • 發佈:2020-10-23
前言
怎麼將一個Callback回撥轉化成Future同步方法(Callback to Future),可以配套async / await去使用呢?
個人覺得,這是一個很常見的現象,不知道為啥,很多人在說明Future用法的時候,都沒提到這個場景,奇怪+懵逼,只能自己去苟解決方案了。
歷程
- 為什麼我要將Callback轉成Future方法?
- 大家知道,Flutter在載入頁面的時候,有個渲染的過程,在沒渲染完成的時候,你去顯示一些View的操作,會報錯的,例如:載入loading彈窗
- 解決方法可能大家都知道,Lifecycle.initState / iniState 生命週期裡面做個延時操作或者使用WidgetsBinding
//延時操作
await Future.delayed(Duration(milliseconds: 200));
//下面可以載入彈窗
//使用WidgetsBinding
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
//此處可以載入彈窗
});
- 當然,使用WidgetsBinding是更靠譜和準確的,但是這個Callback就讓我很方了,而且,這名字太長,也不太記的住,這就需要將它封裝了
- 封裝WidgetsBinding
- 蛋筒了,這玩意怎麼封裝呢
class ViewUtil { ///介面初始化完成 static Future<Void> initFinish() async { WidgetsBinding.instance.addPostFrameCallback((timeStamp) { }); } }
- 首先我想到了:Future.delayed()
- 進去看下他的原始碼
- 有戲,可以看到,這裡面明顯包含了一個Timer中的Callback回撥,但最後轉換成了,Future方法
factory Future.delayed(Duration duration, [FutureOr<T> computation()?]) { if (computation == null && !typeAcceptsNull<T>()) { throw ArgumentError.value( null, "computation", "The type parameter is not nullable"); } _Future<T> result = new _Future<T>(); new Timer(duration, () { if (computation == null) { result._complete(null as T); } else { try { result._complete(computation()); } catch (e, s) { _completeWithErrorCallback(result, e, s); } } }); return result; }
-
分析下
- 首先是例項化一個_Future
()物件,然後返回了這個_Future ()物件 - 可以看到方法的最下方是直接返回這個物件,可想而知,這地方,肯定一直處於一個阻塞狀態,在等待一個條件結束這個阻塞狀態
- 然後在Timer的延遲時間到了後,其回撥中使用了_complete()這個方法,這個方法應該是結束了_Future
()物件的阻塞狀態,然後再返回_Future ()物件,同時這個方法也結束了
- 首先是例項化一個_Future
-
這不就簡單了,我把這個抄出來不就歐了
-
這個_Future類是個私有方法,在future_impl.dart檔案,把這個檔案拷出來,放在我們工具類檔案同一個包下,
- 然後。。。
-
這一堆報錯,玩毛線啊,肯定是我開啟的方式不對
- 難道要一個一個去解決這些報錯?要是這麼麻煩,還搞毛線!
- 是不是我搜索的姿勢不對,再來搜搜看
- 我去,還自動給我提示:dart callback to future,這麼神奇的嗎?試試看
- 然後成功找到這個:https://medium.com/@brianschardt/dart-turn-callback-function-into-a-future-95f54431936b
- 言簡意賅,簡潔明瞭
解決方案
- 記錄下Callback to Future的寫法,很簡單,使用Completer類即可
class ViewUtil {
///介面初始化完成
static Future<Void> initFinish() async {
Completer<Void> completer = Completer();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
completer.complete();
});
return completer.future;
}
}
- 使用
- 使用起來,瞬間簡單很多
void _init() async {
await ViewUtil.initFinish();
///下面可以使用載入彈窗
}
說明
- Future
和Completer 的泛型最好保持一致 - 例如都是String的話,complete()方法裡面就可以加上相應的內容,然後await接受這個方法時候,就能拿到complete()方法裡面輸入的值了
class ViewUtil {
///介面初始化完成
static Future<String> initFinish() async {
Completer<String> completer = Completer();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
completer.complete("測試一下...");
});
return completer.future;
}
}
void _init() async {
var s = await ViewUtil.initFinish();
print(s);
}