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 rootstatic Zone _currentZone 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可以帮助理解: