您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
Flutter异步编程中Completer的使用
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
Flutter异步编程中Completer的使用
自猿其说Tech
2021-06-01
IP归属:未知
497000浏览
计算机编程
Flutter
# Completer的说明 Flutter为我们提供了强大的异步编程能力,相比iOS和Android,极大方便了开发人员的工作,其中我们最常用到的有Future、async、await等,这些基本可以解决大多数的异步问题。但是在一些场景中上面的工具还不能很好的解决问题,而Completer则提供了另一个简便好用的工具,在一些特定的场景可以极大的简化我们的工作。 Completer类由dart:async提供,是另一种创建Future的方式。它在内部创建一个 Future ,并允许你在一段时间后再主动完成它。 Completer和Future的区别在于我们可以手动控制完成时机。使用Future时,我们在向队列添加任务之后,只能被动的通过注册的回调方法接收结果或者处理错误。而 Completer提供了一系列接口允许我们对 Future 的执行过程进行更灵活的控制。 和async、await不同的是,Completer并非关键词,而是一个类,这也意味着通过创建并持有Completer对象, 你就可以在代码的任何地方调用此对象的complet方法来控制Completer对象拥有的future对象的成功或者失败。 我们构建一个新的Completer对象后立即向调用方返回Completer对象内的Future对象,此时调用Completer的代码的行为会像调用普通的future方法一样,一直等待,直到你调用complete方法后,才会取得值并继续执行。而complet方法和completeError方法的参数就是future的返回值。 一般使用 Completer()构造的对象是一个_AsyncCompleter的实例,它的 complete 方法是异步的,这意味着 future 里注册的回调不会立刻执行,而是会延迟到下一个 microtask 。如果你有特殊的需求要求同步完成,需要使用 Completer.sync 方法进行构造,它拿到的是 _SyncCompleter 对象实例。 下面是Completer类以及相关类的源代码: ``` abstract class Completer<T> { factory Completer() => new _AsyncCompleter<T>(); factory Completer.sync() => new _SyncCompleter<T>(); void complete([FutureOr<T>? value]); void completeError(Object error, [StackTrace? stackTrace]); bool get isCompleted; } class _AsyncCompleter<T> extends _Completer<T> { void complete([FutureOr<T>? value]) { if (!future._mayComplete) throw new StateError("Future already completed"); future._asyncComplete(value == null ? value as dynamic : value); } void _completeError(Object error, StackTrace stackTrace) { future._asyncCompleteError(error, stackTrace); } } class _SyncCompleter<T> extends _Completer<T> { void complete([FutureOr<T>? value]) { if (!future._mayComplete) throw new StateError("Future already completed"); future._complete(value == null ? value as dynamic : value); } void _completeError(Object error, StackTrace stackTrace) { future._completeError(error, stackTrace); } } ``` 从源代码可以看出,Completer实际是一个抽象类,Completer()方法实际返回的是一个_AsyncCompleter对象,而Completer.sync()实际返回的是_SyncCompleter对象。而_AsyncCompleter和_SyncCompleter类的实现也非常简单,基本上就是对future的内部方法的一个简单封装。 需要注意的是,宣告完成的complete或completeError方法只能调用一次, 不然会报错。比较保险的作法是在调用完complete或completeError方法后,将Completer对象置为null,在调用complete或completeError方法时,则先判断Completer对象是否为null。 使用 Completer,你只需要这三步: 一、创建 Completer对象并持有 二、把 Complre 分发出去 三、调用 completer.complete 或者 completer.completeError 如下: ``` Completer _myCompleter = Completer(); Future startSomething() { //此处作需要异步操作的事情 //此处不会等complete方法的调用,立即向调用方回传completer的future对象。 return _myCompleter.future; } // 完成时调用此方法 void endSomething() { _myCompleter.complete(); } // 出错调用此方法. void _errorHappened(error) { _completer.completeError(error); } ``` # 典型使用场景 ### 使用场景一 将Callback回调转化成Future同步方法(Callback to Future) Future相对于调度回调函数来说,缓减了回调地狱的问题。 #### 示例一 ``` 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<Results> costlyQuery() { var completer = new Completer(); database.query("SELECT * FROM giant_table", (results) { // when complete completer.complete(results); }, (error) { completer.completeException(error); }); //在查询结束之前 ,这里立即返回。 return completer.future; } ``` #### 示例三 ``` Future openImagePicker () { Complete completer = new Completer(); // ImagePicker 是一个图片选择插件 ImagePicker.singlePicker( context, singleCallback: (data) { completer.complete(data); }, failCallback:(err) { completer.catchError(err); } ); return completer.future; } ``` 使用 ``` openImagePicker().then((data) {}).catchError((err){}); ``` #### 示例四 ``` Future store(Stream stream, EventSink sink) { var completer = new Completer(); stream.listen(sink.add, onError: sink.addError, onDone: () { sink.close(); completer.complete(); }); return completer.future; } ``` ### 使用场景二 用于等待用户或系统在操作完成后才能返回的操作。比如选图片,等待用户输入内容。这些场景中,如果调用的方法是用回调的方式来返回值的话,则和场景一是重合的。 #### 示例一 webview_flutter中的示例: 1.定义 ``` final Completer<WebViewController> _controller = Completer<WebViewController>(); ``` 2.关键,webView加载完成后,调用completer的complete方法,并把webView的webViewController作为参数传给使用方。webViewController就是一个普通对象,没有任何的特殊。 ``` WebView( onWebViewCreated: (WebViewController webViewController) { _controller.complete(webViewController); }, ) ``` 3.使用 ``` final Future<WebViewController> _webViewControllerFuture; @override Widget build(BuildContext context) { return FutureBuilder<WebViewController>( future: _webViewControllerFuture, builder:(BuildContext context, AsyncSnapshot<WebViewController> snapshot) { final bool webViewReady = snapshot.connectionState == ConnectionState.done; final WebViewController controller = snapshot.data; } ) } ``` ### 使用场景三 异步操作涉及到多个操作,在任一个操作中,都有可能返回操作结果,这种情况下使用completer可以降低代码的复杂度,实现时更灵活。 #### 示例一 ``` library execution_queue; import 'dart:async'; /// Queue element wrapper class _Item { final completer; final job; _Item(this.completer, this.job); } /// Queue async jobs to run one after another class ExecutionQueue { List<_Item> _queue = []; bool _active = false; // check if ready for next job void _check() async { if (!_active && _queue.isNotEmpty) { this._active = true; var item = _queue.removeAt(0); try { var result = await item.job(); item.completer.complete(result); } catch (e) { item.completer.completeError(e); } this._active = false; this._check(); } } /// add async job to queue Future<T> add<T>(Function job) { var completer = Completer<T>(); this._queue.add(_Item(completer, job)); this._check(); return completer.future; } } ``` # 不当使用Completer的场景 避免直接使用 Completer。 很多异步编程的新手想要编写生成一个 future 的代码。而 Future 的构造函数看起来并不满足他们的要求,然后他们就发现 Completer 类并使用它: 错误用法: ``` Future<bool> fileContainsBear(String path) { var completer = Completer<bool>(); File(path).readAsString().then((contents) { completer.complete(contents.contains('bear')); }); return completer.future; } ``` Completer 是用于两种底层代码的:新的异步原子操作和集成没有使用 Future 的异步代码。大部分的代码都应该使用 async/await 或者 Future.then(),这样代码更加清晰并且异常处理更加容易。上面的使用方式实际并不必需用到completer,用then或wait就可以实现,使用completer不仅增加了代码的复杂度,而且使也降低了代码的执行效率。 正确用法一: ``` Future<bool> fileContainsBear(String path) { return File(path).readAsString().then((contents) { return contents.contains('bear'); }); } ``` 正确用法二: ``` Future<bool> fileContainsBear(String path) async { var contents = await File(path).readAsString(); return contents.contains('bear'); } ``` # 总结 上面我们初步的介绍了completer的原理和常用的使用场景。如果您想继续深入了解Completer,并更灵活的使用Completer的话,您可以多看一下Flutter的源码,Flutter中对Completer的使用非常多,实际上是一个非常常用的异步工具,您可以在Flutter系统库中的。相信您在熟练的掌握Completer后,可以使您在使用Flutter的异步编程时更加得心应手。 ------------ ###### 自猿其说Tech-JDL京东物流技术发展部 ###### 作者:用户产品部 高勇鹏 ------------
原创文章,需联系作者,授权转载
上一篇:最高评级!京东行云DevOps平台获得信通院先进级平台认证
下一篇:Flutter状态管理之InheritedWidget&Provider
相关文章
Taro小程序跨端开发入门实战
Flutter For Web实践
配运基础数据缓存瘦身实践
自猿其说Tech
文章数
426
阅读量
2149964
作者其他文章
01
深入JDK中的Optional
本文将从Optional所解决的问题开始,逐层解剖,由浅入深,文中会出现Optioanl方法之间的对比,实践,误用情况分析,优缺点等。与大家一起,对这项Java8中的新特性,进行理解和深入。
01
Taro小程序跨端开发入门实战
为了让小程序开发更简单,更高效,我们采用 Taro 作为首选框架,我们将使用 Taro 的实践经验整理了出来,主要内容围绕着什么是 Taro,为什么用 Taro,以及 Taro 如何使用(正确使用的姿势),还有 Taro 背后的一些设计思想来进行展开,让大家能够对 Taro 有个完整的认识。
01
Flutter For Web实践
Flutter For Web 已经发布一年多时间,它的发布意味着我们可以真正地使用一套代码、一套资源部署整个大前端系统(包括:iOS、Android、Web)。渠道研发组经过一段时间的探索,使用Flutter For Web技术开发了移动端可视化编程平台—Flutter乐高,在这里希望和大家分享下使用Flutter For Web实践过程和踩坑实践
01
配运基础数据缓存瘦身实践
在基础数据的常规能力当中,数据的存取是最基础也是最重要的能力,为了整体提高数据的读取能力,缓存技术在基础数据的场景中得到了广泛的使用,下面会重点展示一下配运组近期针对数据缓存做的瘦身实践。
自猿其说Tech
文章数
426
阅读量
2149964
作者其他文章
01
深入JDK中的Optional
01
Taro小程序跨端开发入门实战
01
Flutter For Web实践
01
配运基础数据缓存瘦身实践
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号