Future
Dart
是以事件驱动的单队列模型,借助Future
能够向队列中添加事件以执行。这里讨论以下两种生成Future
的方式:
factory Future(FutureOr<T> computation())
factory Future.delayed(Duration duration, [FutureOr<T> computation()])
首先看下两种方式的源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
factory Future(FutureOr<T> computation()) {
_Future<T> result = new _Future<T>();
Timer.run(() {
try {
result._complete(computation());
} catch (e, s) {
_completeWithErrorCallback(result, e, s);
}
});
return result;
}
factory Future.delayed(Duration duration, [FutureOr<T> computation()]) {
_Future<T> result = new _Future<T>();
new Timer(duration, () {
if (computation == null) {
result._complete(null);
} else {
try {
result._complete(computation());
} catch (e, s) {
_completeWithErrorCallback(result, e, s);
}
}
});
return result;
}
第一种方式下,直接调用的是Timer.run
, 它本质是去创建一个Timer
:
1
2
3
static void run(void callback()) {
new Timer(Duration.zero, callback);
}
可以得到一个简单的结论:这两种方式本质是一样的,前者只是后者的简化。如:
1
2
3
4
5
6
7
Future(() {
print("test");
});
//等于
Future.delayed(Duration.zero, () {
print("test");
});
Future.delayed
聚焦于Future.delayed
。
看源码,首先是实例出一个_Future
,它的源码片段:
1
2
3
4
5
6
7
8
class _Future<T> implements Future<T> {
final Zone _zone;
//...
_Future() : _zone = Zone.current;
//...
}
构造函数里啥都没做,只是初始化了一下_zone
。Zone
类似于Java
中的ThreadLocal
, 它的作用除了可以存储外,最主要的作用是提供一个异步代码调用的环境。
接着,实例化Timer
,源码片段:
1
2
3
4
5
6
7
8
9
factory Timer(Duration duration, void callback()) {
if (Zone.current == Zone.root) {
// No need to bind the callback. We know that the root's timer will
// be invoked in the root zone.
return Zone.current.createTimer(duration, callback);
}
return Zone.current
.createTimer(duration, Zone.current.bindCallbackGuarded(callback));
}
这里又是Zone
相关的代码。我们先跳过,只需大致了解这里会创建一个Timer
,在duration
时间之后,会调用callback()
。
传入Timer
的callback()
逻辑中,最主要的逻辑是:
1
result._complete(computation());
跟进去:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void _complete(FutureOr<T> value) {
assert(!_isComplete);
if (value is Future<T>) {
if (value is _Future<T>) {
_chainCoreFuture(value, this);
} else {
_chainForeignFuture(value, this);
}
} else {
_FutureListener listeners = _removeListeners();
_setValue(value);
_propagateToListeners(this, listeners);
}
}
FutureOr<T> value
表示value
的类型可能是Future<T>
或者T
如果是T
,直接完成并通知;如果是Future<T>
会等待这个Future
结束,如此链式下去。
看个例子:
1
2
3
4
5
6
7
8
9
10
11
12
main() {
Future(() {
print("outside");
return Future.delayed(Duration(seconds: 2), () {
print("inside");
}).whenComplete(() {
print("inside completed");
});
}).whenComplete(() {
print("outside completed");
});
}
打印如下:
1
2
3
4
outside
inside
inside completed
outside completed
简单的结论:Future
只有等它的所有嵌套子Future
完成之后,它才算完成。
最后,Future.delay
返回了最开始创建的_Future
实例。
总结下Future.delay
的流程:
创建
_Future
实例:result
。创建延时为
duration
的Timer
。并在时间到达时,判断computation
的返回类型:直接结束或者链式调用下去。返回
_Future
实例。
Zone
整体流程梳理完之后,跳回去看Timer
的实现。不过在这之前,先来了解下Zone
。
上面有提到Zone
的作用是提供一个异步代码调用的环境。这比较难理解,看一个例子:
1
2
3
4
5
6
7
8
9
main() {
runZoned(() {
print("test");
}, zoneSpecification: ZoneSpecification(
print: (self, parent, zone, s) {
parent.print(zone, "hook it: $s");
}
));
}
打印如下:
1
hook it: test
这个例子显示了如何利用Zone
去hook print
函数。
具体的做法就是通过runZoned
创建了一个自定义的Zone
,并让我们的代码运行在这个Zone
的环境下。
Zone
的部分源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
abstract class Zone {
//...
static const Zone root = _rootZone;
static Zone _current = _rootZone;
Zone get parent;
Zone fork({ZoneSpecification specification, Map zoneValues});
Timer createTimer(Duration duration, void callback());
void print(String line);
ZoneCallback<R> registerCallback<R>(R callback());
//...
}
三个变量:
static const Zone root
static Zone _current
Zone parent
其中,root
是常量,表示最根部的Zone
。main()
函数的环境就是Zone root
。默认下_current
与root
一致。
parent
若是null
, 说明该zone
就是root
;其他的zone
的parent
都不会是null
。要想创建一个新的Zone
,只能通过现有的zone
去fork
一份,新fork
出来的zone
的parent
就是调用fork
的zone
。
可以得到一个结论:root
是其他所有zone
的祖先。
fork
方法通过specification
来hook
parent 的相关功能方法,来达到不一样的效果;第二个参数Map zoneValues
可以理解为新fork
出来的zone
的环境变量,通过zone[key]
来存取。
createTimer
方法是不是感觉很熟悉?Future
最后就是调用到这里。
print
方法跟我们平常调用的print()
方法有什么联系呢?
看下平常调用的print()
源码:
1
2
3
4
5
6
7
8
void print(Object object) {
String line = "$object";
if (printToZone == null) {
printToConsole(line);
} else {
printToZone(line);
}
}
而在fork zone
时,设置了printToZone
:
1
2
3
4
5
6
7
8
9
10
Zone _rootFork(Zone self, ZoneDelegate parent, Zone zone,
ZoneSpecification specification, Map zoneValues) {
//...
printToZone = _printToZone;
//...
}
void _printToZone(String line) {
Zone.current.print(line);
}
常用的print()
方法最后调用的是Zone.current.print(line)
,这两个方法就关联了起来。看到这里,对上面hook print
的例子是不是理解到了?
registerCallback
的主要作用简单理解就是去wrap 原来的callback
。
Zone
的继承结构:
Zone
_Zone
_RootZone
_CustomZone
_Zone
的源码很简单:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
abstract class _Zone implements Zone {
const _Zone();
// TODO(floitsch): the types of the `_ZoneFunction`s should have a type for
// all fields.
_ZoneFunction<Function> get _run;
_ZoneFunction<Function> get _runUnary;
_ZoneFunction<Function> get _runBinary;
_ZoneFunction<Function> get _registerCallback;
_ZoneFunction<Function> get _registerUnaryCallback;
_ZoneFunction<Function> get _registerBinaryCallback;
_ZoneFunction<ErrorCallbackHandler> get _errorCallback;
_ZoneFunction<ScheduleMicrotaskHandler> get _scheduleMicrotask;
_ZoneFunction<CreateTimerHandler> get _createTimer;
_ZoneFunction<CreatePeriodicTimerHandler> get _createPeriodicTimer;
_ZoneFunction<PrintHandler> get _print;
_ZoneFunction<ForkHandler> get _fork;
_ZoneFunction<HandleUncaughtErrorHandler> get _handleUncaughtError;
_Zone get parent;
ZoneDelegate get _delegate;
Map get _map;
bool inSameErrorZone(Zone otherZone) {
return identical(this, otherZone) ||
identical(errorZone, otherZone.errorZone);
}
}
_ZoneFunction<T>
的定义如下:
1
2
3
4
5
class _ZoneFunction<T extends Function> {
final _Zone zone;
final T function;
const _ZoneFunction(this.zone, this.function);
}
_Zone
不仅没帮Zone
实现任何功能,相反还搞出一堆_ZoneFunction<T>
,有点猪队友的赶脚。那真相是啥?真相是真香,_Zone
定义的这些变量是实现hook
的基础啊。
来看下_CustomZone
的源码片段:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class _CustomZone extends _Zone {
//...
_CustomZone(this.parent, ZoneSpecification specification, this._map) {
//...
_print = (specification.print != null)
? new _ZoneFunction<PrintHandler>(this, specification.print)
: parent._print;
//...
}
//...
void print(String line) {
var implementation = this._print;
assert(implementation != null);
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
PrintHandler handler = implementation.function;
return handler(implementation.zone, parentDelegate, this, line);
}
//...
}
以print
为例,创建_CustomZone
时传入了ZoneSpecification
, 若ZoneSpecification
有print
的定义,就把该执行逻辑保存到_print
里面。当调用Zone.print
时在从_print
中拿出来。
Zone
的内容还有很多,这里就先打住,以后有时间在继续探索。
Timer
看完Zone
, 在回头看看Timer
。在贴一遍源码:
1
2
3
4
5
6
7
8
9
factory Timer(Duration duration, void callback()) {
if (Zone.current == Zone.root) {
// No need to bind the callback. We know that the root's timer will
// be invoked in the root zone.
return Zone.current.createTimer(duration, callback);
}
return Zone.current
.createTimer(duration, Zone.current.bindCallbackGuarded(callback));
}
来理解一下:
- 若
current
与root
一致,直接调用createTimer
- 否则,先
bindCallbackGuarded
, 然后在createTimer
差别很明显,在于要不要bindCallbackGuarded
?
原因也很简单,如果是root
, 它的registerCallback
默认是没有加逻辑的,具体可以看_RootZone
源码,你传入什么就传出什么,因此不需要去wrap callback
; 而如果current zone
不是root zone
的环境,那它的registerCallback
是有可能有新的实现,顾需要先调用bindCallbackGuarded
去wrap一下。
最后有点小疑惑,Zone.createTimer
最后回到了Timer._createTimer
中,相关源码如下:
1
2
3
4
5
6
7
abstract class Timer {
//...
external static Timer _createTimer(Duration duration, void callback());
//...
}
跟到最后也没有_createTimer
的具体实现。而关键字external
也不是很懂,感觉像是要让外部提供该实现。
Demo代码
关于Zone
、Timer
、Event-Loop
的关系,该demo可以帮助理解: