前言
怎么將一個Callback回調(diào)轉(zhuǎn)化成Future同步方法(Callback to Future),可以配套async / await去使用呢?
個人覺得,這是一個很常見的現(xiàn)象,不知道為啥,很多人在說明Future用法的時候,都沒提到這個場景,奇怪+懵逼,只能自己去茍解決方案了。
實(shí)現(xiàn)
不多嗶嗶,先看實(shí)現(xiàn),趕時間的靚仔,可以直接忽略掉歷程描述
- 記錄下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<T>和Completer<T>的泛型最好保持一致
- 例如都是String的話,complete()方法里面就可以加上相應(yīng)的內(nèi)容,然后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);
}
歷程
- 為什么我要將Callback轉(zhuǎn)成Future方法?
- 大家知道,F(xiàn)lutter在加載頁面的時候,有個渲染的過程,在沒渲染完成的時候,你去顯示一些View的操作,會報錯的,例如:加載loading彈窗
- 解決方法可能大家都知道,Lifecycle.initState / iniState 生命周期里面做個延時操作或者使用WidgetsBinding
//延時操作
await Future.delayed(Duration(milliseconds: 200));
//下面可以加載彈窗
//使用WidgetsBinding
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
//此處可以加載彈窗
});
- 當(dāng)然,使用WidgetsBinding是更靠譜和準(zhǔn)確的,但是這個Callback就讓我很方了,而且,這名字太長,也不太記的住,這就需要將它封裝了
- 封裝WidgetsBinding
- 蛋筒了,這玩意怎么封裝呢
class ViewUtil {
///界面初始化完成
static Future<Void> initFinish() async {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
});
}
}
- 首先我想到了:Future.delayed()
- 進(jìn)去看下他的源碼
- 有戲,可以看到,這里面明顯包含了一個Timer中的Callback回調(diào),但最后轉(zhuǎn)換成了,F(xiàn)uture方法
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;
}
-
分析下
- 首先是實(shí)例化一個_Future<T>()對象,然后返回了這個_Future<T>()對象
- 可以看到方法的最下方是直接返回這個對象,可想而知,這地方,肯定一直處于一個阻塞狀態(tài),在等待一個條件結(jié)束這個阻塞狀態(tài)
- 然后在Timer的延遲時間到了后,其回調(diào)中使用了_complete()這個方法,這個方法應(yīng)該是結(jié)束了_Future<T>()對象的阻塞狀態(tài),然后再返回_Future<T>()對象,同時這個方法也結(jié)束了
這不就簡單了,我把這個抄出來不就歐了

img
-
這個_Future類是個私有方法,在future_impl.dart文件,把這個文件拷出來,放在我們工具類文件同一個包下,
- 然后。。。
image-20201023140910251 -
這一堆報錯,玩毛線啊,肯定是我打開的方式不對
- 難道要一個一個去解決這些報錯?要是這么麻煩,還搞毛線!

image-20201023144223906
- 是不是我搜索的姿勢不對,再來搜搜看
- 我去,還自動給我提示:dart callback to future,這么神奇的嗎?試試看

image-20201023143143286
- 然后成功找到這個:Dart: Turn Callback Functions into a Futures! 2018, Flutter!!
- 言簡意賅,簡潔明了

img
