Flutter心得 -- 从JavaScript的回调到Dart的Future
最近由于其它项目紧急,Flutter的摸索断断续续,并没有实质性的进展。今天抽空要将之前js的一些逻辑改到dart中,发现一个纠结的问题:
JavaScript的回调函数怎么优雅地转到Dart ?
正常来讲,js的回调,其实是可以转化为Promise, 那么Promise对应Dart的Future是显而易见的,有什么纠结的呢?
const callback=()=>{
console.log('这里是回调');
}
var promise = new Promise((resolve, reject)=>{
resolve();
});
promise.then(callback);Function callback = () {
print('这里是回调');
};
var future = Future.delayed(Duration(seconds: 2), (){
print('future');
});
future.then((val){
callback();
});但是有些js回调是不方便直接转化为Promise的
var isLogining=false;
var isLogin=false;
var successQueue=[];
function login(callback){
if(isLogin){
callback();
return;
}
successQueue.push(callback);
if(isLogging){
return;
}
isLogging = true;
//async do login
isLogin=true;
isLogging =false;
var func;
while(func=successQueue.shift()){
func();
}
}
//调用
login(()=>{
//处理登录后的事情
});基本逻辑就是全局有一个自动登录操作,其它地方操作需要先经过登录操作验证,如果验证成功就回调
为防止多个调用同时进行时登录操作冲突,就需要记录一个是否在登录的状态,如果有同时的调用,就加入队列,登录操作成功后统一调用。
这个逻辑,要改Promise,是可以利用Promise参数的回调实现的
var isLogining=false;
var isLogin=false;
var successQueue=[];
function login(){
if(isLogin){
return new Promise((resolve, reject)=>{
if(isLogin){
resolve();
}else{
reject();
}
});
}
if(isLogging){
return new Promise((resolve, reject)=>{
successQueue.push(()=>{
if(isLogin){
resolve();
}else{
reject();
}
});
});
}
isLogging = true;
//async do login
return Promise((resolve, reject)=>{
isLogin=true;
isLogging =false;
var func;
while(func=successQueue.shift()){
func();
}
resolve();
});
}原理就是利用创建Promise时的回调参数,再次进行事件监听回调
但是,Dart的Future并没有回调参数,怎么实现呢?
搜索了下网友的相关文章,基本都是介绍Future, async, await的基本用法的。
思路1:在Future中循环延时判断
感觉方法有点蠢,具体逻辑未实现
思路2:ChangeNotifyer
这是一个专门做状态管理的通知类,但是还是没办法和Future结合,如果用回调函数的方式,是可以做事件通知并回调。
但这个回调没办法用Future实现
正解:Completer
这是async包中的一个类,专门做事件通知的,完美结合了Future类
var complete = Completer();
complete.future.then((val) {
print('future success:' + val);
});
print('before delay');
var future = Future.delayed(Duration(seconds: 2), () {
complete.complete('aaa');
print('after complete');
complete.future.then((val) {
print('future3 success:' + val);
});
complete = Completer();
complete.future.then((val) {
print('future reconstruct success:' + val);
});
complete.complete('bbb');
});
print('after delay');
complete.future.then((val) {
print('future2 success:' + val);
});可以看到,创建了Completer实例后,可以在实例的future属性上追加处理。
当调用了 complete方法后,就会依次处理相应的任务。
1. complete只能调用一次。
2. complete调用后,新追加的处理,依然可以执行,而且是马上执行
要重置事件,重新实例化就可以了。
这就完美契合了之前的需求,不需要用回调队列来伪装Future了
class LoginState{
var isLogining=false;
var isLogin=false;
var loginComplete;
Future<bool> login(){
if(isLogin || isLogging){
return loginComplete.future;
}
isLogging = true;
loginComplete = Completer();
//async do login
return Future.delayed(Duration(seconds: 2), ()=>{
isLogin=true;
isLogging =false;
loginComplete.complete(true);
return true;
});
}
}注:相关代码仅用于演示,可能不符合实际需求中的逻辑。