同步与异步
模拟一个耗时操作,来看一下下面例子:
同步
在这个例子中,我们用 doSomeThingNormal 模拟一个耗时操作,该操作需要两秒后才能返回数据。
doSomeThingNormal() {
sleep(Duration(seconds: 2));
return "Normal---->doSomeThing";
}
main() {
print("main---->start");
print(doSomeThingNormal());
print("main---->end");
}
运行
doSomeThingNormal 会阻塞执行两秒后,才会继续执行,这会导致卡顿,显然不是我们想要的效果。
flutter: main---->start
//停顿两秒
flutter: Normal---->doSomeThing
flutter: main---->end
异步
我们再来看看异步,我们使用关键字 async,使用 async 关键字的方法会返回一个 Future 对象,我们做如下改动:
doSomeThingAsync() async {
return Future<String>(() {
sleep(Duration(seconds: 2));
return "Async---->doSomeThing";
});
}
main1() {
print("main---->start");
print(doSomeThingAsync());
print("main---->end");
}
运行
flutter: main---->start
flutter: Instance of 'Future<dynamic>'
flutter: main---->end
发现程序丝滑般顺畅。So,
Dart 是一门单线程语言,在遇到延迟的运算(网络请求,IO 操作)时,线程按顺序执行时就会阻塞,导致用户觉得界面卡顿,这显然是不被允许的,那如何处理这些耗时操作呢?
- 多线程: 如 Java,一般都会新开一个线程(Thread),在新线程中处理完耗时操作,然后在把操作结果通过 Looper/handler 机制传递给主线程。
- 单线程:主要通过单线程+事件循环,如 Dart,JavaScript 都是基于单线程加事件循环来处理耗时操作。
什么是事件循环
Dart 的事件循环如下图,循环中有两个队列,一个是微任务队列 (MicroTask queue), 一个是事件队列(Event queue)。
- 事件队列包含外部事件,例如 I/O, Timer,点击,网络事件等等。
- 微任务队列则包含有 Dart 内部的微任务,主要是通过 scheduleMicrotask 来调度。

如图可知事件的循环过程如下:
- 当程序启动,首先会处理完微任务队列里的所有任务。
- 当所有微任务处理完后,会从事件队列里取一个事件进行处理。
- 回到微任务队列继续循环
总之,微任务队列会一次性全部处理,事件队列则每次只取一个处理。
了解这个流程,我们才能理解 Dart 代码的执行顺序。
如何把放在微任务队列
Dart 中提供 scheduleMicrotask 方法,让代码以微任务的方式执行。
scheduleMicrotask((){
print("A Microtask");
});
如何把代码放到事件队列
事件队列包含外部事件,例如 I/O, Timer,点击,网络事件,异步方法等
async_event() async{
print("normal_event");
}
那么如下代码的运行顺序你清楚吗,”normal_event“会被首先打印吗?
main2() {
//事件队列
async_event();
//微任务队列
foo();
}
foo() {
print("foo");
scheduleMicrotask(foo);
}
async_event() async {
print("normal_event");
}
答案是不会打印”normal_event“,因为微任务队列中一直有任务,事件队列没有机会运行。
Dart 实现异步的方式
- 关键字 async,await
- Future
async/await
上面的例子中我们已经见到过 async 关键字,那 await 怎么用?细心的你可能已经发现上面:
main1() {
print("main---->start");
print(doSomeThingAsync());
print("main---->end");
}
doSomeThingAsync()打印出来的结果是:
flutter: Instance of 'Future<dynamic>'
是一个 Future 对象,但我们想要的其实是字符串对象。如何获取字符串对象?我们可以用 await 接收它,做如下改动:
doThings() async {
String msg = await doSomeThingAsync();
print(msg);
}
main4() {
print("main---->start");
doThings();
print("main---->end");
}
可见我们又抽出一个异步方法 doThings,因为 规定 await 必须在 async 修饰的方法中使用 ,因此 doThings 方法必须用 async 修饰。
- await 关键字必须在 async 函数内部使用
运行
flutter: main---->start
flutter: main---->end
//等待两秒
flutter: Async---->doSomeThing
我把这个小案例的代码抽一下,看看它的执行顺序:
fun1() async {
print("fun1---->start");
String msg = await fun2();
print("fun1:msg:\t${msg}");
}
fun2() async {
print("fun2---->");
return "Hello world";
}
main5() {
print("main---->start");
fun1();
print("main---->end");
}
输出
flutter: main---->start
flutter: fun1---->start
flutter: fun2---->
flutter: main---->end
flutter: fun1:msg: Hello world
async/await 的好处就是让异步的写法上类似于同步方法,同样的它有不好的一面,就是这样很容易陷入”回调地域“,由 fun1 fun2 的例子可见一斑,如果 fun2 的输出还依赖其他异步函数,那程序可读性将大打折扣,一般异步操作都伴随着 try catch ,程序在包一层 try catch 那 async/await 这种写法就不那么友好了,为了避免这样的问题,JS 引入了 Promise。而 Dart, 引入了 Future。
Future
这里简单的介绍一下 Future 的使用。
- 创建一个立刻在事件队列运行的 Future
Future(() => print("立刻在Event queue中运行的Future"));
- 创建一个延时在事件队列运行的 Future
Future.delayed(
Duration(seconds: 2), () => print("2秒后在Event queue中运行的Future"));
- 创建一个在微任务队列里运行的 Future
Future.microtask(() => print('在Microtask queue里运行的Future'));
- 创建一个同步运行的 Future
Future.sync(() => print('同步运行的Future'));
代码
main6() {
Future(() => print("立刻在Event queue中运行的Future"));
Future.delayed(
Duration(seconds: 2), () => print("2秒后在Event queue中运行的Future"));
Future.microtask(() => print('在Microtask queue里运行的Future'));
Future.sync(() => print('同步运行的Future'));
}
运行
flutter: 同步运行的Future
flutter: 在Microtask queue里运行的Future
flutter: 立刻在Event queue中运行的Future
flutter: 2秒后在Event queue中运行的Future
由执行结果的先后顺序能使事件机制更深刻一点。
链式调用
通过调用 then 来把回调函数串起来,这样就解决了"回调地狱"的问题。
Future(()=> print('task'))
.then((_)=> print('callback1'))
.then((_)=> print('callback2'));
异常处理
- onError 捕获异常处理
//链式调用,捕获异常
Future(() => print("立刻在Event queue中运行的Future")).then(fun1(), onError: (e) {
handleError(e);
}).then(fun2(), onError: (e) {
handleError(e);
});
Future(()=> throw 'we have a problem')
.then((_)=> print('callback1'))
.then((_)=> print('callback2'))
.catchError((error)=>print('$error'));
- 类似 Java finally 的机制
Future(()=> throw 'we have a problem')
.then((_)=> print('callback1'))
.then((_)=> print('callback2'))
.catchError((error)=>print('$error'))
.whenComplete(()=> print('whenComplete'));
无论这个 Future 是正常执行完毕还是抛出异常,whenComplete 都一定会被执行。
Future 的两种状态
Future 在执行的整个过程中,我们通常把它划分成了两种状态:
未完成状态(uncompleted)
- 执行 Future 内部的操作时,我们称这个过程为未完成状态
完成状态(completed)
- 当 Future 内部的操作执行完成,通常会返回一个值,或者抛出一个异常,此时称 Future 为完成状态。
以上的案例我们发现我们并不能手动控制 Future 的完成状态,都是靠系统调度完成之后置 Future 的状态为完成。除此之外,我们还可以用 Completer 稍微控制一下。
main8() {
// 实例化一个Completer
var completer = Completer();
// 这里可以拿到这个completer内部的Future
var future = completer.future;
future.then((_)=>print("then"));
//...someThing elses
completer.complete("finished");
}
但这个没发现有多大用,Dart 的异步我其实只说了一些很浅显的点,背后还有很多东西,如 Isolate,Flutter 的引擎线程模式。这以后有机会我在研究一下,学海无涯啊。
最后
贴一张自己学习Flutter的公众号,感兴趣的小伙伴可以一起学习哦。。。

全部代码
import 'dart:async';
import 'dart:ffi';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_travel/pages/dart_pages/dart_oop_page.dart';
class DartAsyncPage extends StatefulWidget {
@override
_DartAsyncPageState createState() => _DartAsyncPageState();
}
class _DartAsyncPageState extends State<DartAsyncPage> {
@override
void initState() {
// TODO: implement initState
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Dart异步"),
),
body: Center(
child: Material(
child: InkWell(
onTap: () {
//main();
//main1();
// main2();
// main4();
//main5();
//main6();
main8();
},
child: Container(
alignment: Alignment.center,
height: 50,
width: 200,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
gradient: LinearGradient(colors: [
Theme.of(context).primaryColor.withOpacity(.5),
Theme.of(context).primaryColor.withOpacity(.7),
])),
child: Text(
"点击运行",
style: TextStyle(color: Colors.white, fontSize: 16),
),
),
),
),
),
);
}
main() {
print("main---->start");
print(doSomeThingNormal());
print("main---->end");
}
main1() {
print("main---->start");
print(doSomeThingAsync());
print("main---->end");
}
doThings() async {
String msg = await doSomeThingAsync();
print(msg);
}
main4() {
print("main---->start");
doThings();
print("main---->end");
}
fun1() async {
print("fun1---->start");
String msg = await fun2();
print("fun1:msg:\t${msg}");
}
fun2() async {
print("fun2---->");
return "Hello world";
}
main5() {
print("main---->start");
fun1();
print("main---->end");
}
main6() {
Future(() => print("立刻在Event queue中运行的Future"));
Future.delayed(
Duration(seconds: 2), () => print("2秒后在Event queue中运行的Future"));
Future.microtask(() => print('在Microtask queue里运行的Future'));
Future.sync(() => print('同步运行的Future'));
}
main7() {
Future(() => print("立刻在Event queue中运行的Future")).then(fun1(), onError: (e) {
handleError(e);
}).then(fun2(), onError: (e) {
handleError(e);
});
}
main8() {
// 实例化一个Completer
var completer = Completer();
// 这里可以拿到这个completer内部的Future
var future = completer.future;
future.then((_)=>print("then"));
//...someThing elses
completer.complete("finished");
}
doSomeThingNormal() {
sleep(Duration(seconds: 2));
return "Normal---->doSomeThing";
}
doSomeThingAsync() async {
return Future<String>(() {
sleep(Duration(seconds: 2));
return "Async---->doSomeThing";
});
}
main2() {
//事件队列
async_event();
//微任务队列
foo();
}
foo() {
print("foo");
scheduleMicrotask(foo);
}
async_event() async {
print("normal_event");
}
//普通
getNormalString() {
return "normal---->string";
}
//异步
getAsyncString() async {
String msg = "async---->string";
return msg;
}
void handleError(e) {
print(e);
}
}
网友评论