Home Dart中Future、Zone、Timer的源码学习
Post
Cancel

Dart中Future、Zone、Timer的源码学习

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;
    //...
    
}

构造函数里啥都没做,只是初始化了一下_zoneZone类似于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()

传入Timercallback()逻辑中,最主要的逻辑是:

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

  • 创建延时为durationTimer。并在时间到达时,判断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是常量,表示最根部的Zonemain()函数的环境就是Zone root。默认下_currentroot一致。

parent若是null, 说明该zone就是root;其他的zoneparent都不会是null。要想创建一个新的Zone,只能通过现有的zonefork一份,新fork出来的zoneparent就是调用forkzone

可以得到一个结论:root是其他所有zone的祖先。

fork方法通过specificationhook 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, 若ZoneSpecificationprint的定义,就把该执行逻辑保存到_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));
  }

来理解一下:

  • currentroot一致,直接调用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代码

关于ZoneTimerEvent-Loop的关系,该demo可以帮助理解:

https://github.com/dart-archive/www.dartlang.org/blob/master/src/tests/site/articles/zones/task_interceptor.dart

This post is licensed under CC BY 4.0 by the author.