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; }); } }
注:相关代码仅用于演示,可能不符合实际需求中的逻辑。