以前說到 Flutter 中與異步相關的 Future,瞭解到,dart 是一個單線程模型的語言,當咱們使用 Future 去實現一個異步操做時,多半是利用單線程的事件循環機制來模擬,相似於 Android 中的 Looper。c++
不過 dart 中的事件循環其實仍是分兩種的,也就是網上常說的兩個循環隊列,microtask queue 和 event queue,在介紹 Future 的那篇文章裏面其實主要說到的是 event queue 這種模式的運做方式,因此打算在此再詳細說下 microtask queue 的一些實現細節,以此來講明,爲什麼 microtask queue 必定會優先於 event queue 執行。markdown
就拿 Future 來講,它就能夠支持跑在兩種隊列上,當咱們直接調用 Future()
或 Future.delayed()
的時候,內部都是經過 Timer
實現的,其內部就會利用 ReceivePort/SendPort 進行一次事件循環,在下一次循環時調用對應的 callback,具體的調用過程在 Future 那篇裏面有說。而後,當咱們調用Future.microtask
建立一個 Future 時,此時它的 callback 就是被添加到 microtask 隊列上的,那麼 callback 的調用時機就會被提早,下面就簡單看下一個 microtask 的執行過程。異步
首先,當咱們使用 Future 建立一個 microtask 時,調用的方法爲:async
// sdk/lib/async/future.dart
factory Future.microtask(FutureOr<T> computation()) {
_Future<T> result = new _Future<T>();
scheduleMicrotask(() {
try {
result._complete(computation());
} catch (e, s) {
_completeWithErrorCallback(result, e, s);
}
});
return result;
}
// sdk/lib/async/schedule_microtask.dart
@pragma('vm:entry-point', 'call')
void scheduleMicrotask(void Function() callback) {
_Zone currentZone = Zone._current;
if (identical(_rootZone, currentZone)) {
// No need to bind the callback. We know that the root's scheduleMicrotask
// will be invoked in the root zone.
_rootScheduleMicrotask(null, null, _rootZone, callback);
return;
}
_ZoneFunction implementation = currentZone._scheduleMicrotask;
if (identical(_rootZone, implementation.zone) &&
_rootZone.inSameErrorZone(currentZone)) {
_rootScheduleMicrotask(
null, null, currentZone, currentZone.registerCallback(callback));
return;
}
Zone.current.scheduleMicrotask(Zone.current.bindCallbackGuarded(callback));
}
複製代碼
結構跟調用 Timer 基本一致,這裏主要是調用 scheduleMicrotask 去建立一個 microtask 的;在 scheduleMicrotask 中根據 Zone 類型不一樣,選擇了不一樣的方式建立 microtask,通常狀況下,就直接看 RootZone 的實現就能夠了,默認狀況下,代碼就是跑在 RootZone 上的。ide
// sdk/lib/async/zone.dart
void _rootScheduleMicrotask(
Zone? self, ZoneDelegate? parent, Zone zone, void f()) {
if (!identical(_rootZone, zone)) {
bool hasErrorHandler = !_rootZone.inSameErrorZone(zone);
if (hasErrorHandler) {
f = zone.bindCallbackGuarded(f);
} else {
f = zone.bindCallback(f);
}
}
_scheduleAsyncCallback(f);
}
// sdk/lib/async/schedule_microtask.dart
void _scheduleAsyncCallback(_AsyncCallback callback) {
_AsyncCallbackEntry newEntry = new _AsyncCallbackEntry(callback);
_AsyncCallbackEntry? lastCallback = _lastCallback;
if (lastCallback == null) {
_nextCallback = _lastCallback = newEntry;
if (!_isInCallbackLoop) {
_AsyncRun._scheduleImmediate(_startMicrotaskLoop);
}
} else {
lastCallback.next = newEntry;
_lastCallback = newEntry;
}
}
複製代碼
以後調用到 _scheduleAsyncCallback,這個函數是比較重要的,_nextCallback 就是 microtask queue 的的隊首,_lastCallback 則是隊尾,這個函數的做用就是將新的 callback 添加到 microtask 隊列中,同時,當此前尚未添加過 microtask 的時候,就須要調用_AsyncRun._scheduleImmediate(_startMicrotaskLoop);
開始一輪 microtask 的執行,_scheduleImmediate 是一個 external 函數,其實如今 sdk/lib/_internal/vm/lib/schedule_microtask_patch.dart 中,函數
@patch
class _AsyncRun {
@patch
static void _scheduleImmediate(void callback()) {
final closure = _ScheduleImmediate._closure;
if (closure == null) {
throw new UnsupportedError("Microtasks are not supported");
}
closure(callback);
}
}
typedef void _ScheduleImmediateClosure(void callback());
class _ScheduleImmediate {
static _ScheduleImmediateClosure? _closure;
}
@pragma("vm:entry-point", "call")
void _setScheduleImmediateClosure(_ScheduleImmediateClosure closure) {
_ScheduleImmediate._closure = closure;
}
複製代碼
這裏的大體流程就是,給 _scheduleImmediate 傳一個 callback,也就是 _startMicrotaskLoop 函數,不過 _scheduleImmediate 也只是轉手將 callback 給了 _ScheduleImmediate._closure 執行,可是 _ScheduleImmediate._closure 是經過 _setScheduleImmediateClosure 賦值的,因此這裏還須要再看 _setScheduleImmediateClosure 是什麼時候被調用。從聲明看,這個函數應該是要在 dart vm 中調用的,在 vm 代碼中搜索找到,在進行 isolate 初始化時,會依此調用oop
DartIsolate::LoadLibraries
DartRuntimeHooks::Install
InitDartAsync
_setScheduleImmediateClosure 就是在這裏被調用的,ui
// dart_runtime_hooks.cc
static void InitDartAsync(Dart_Handle builtin_library, bool is_ui_isolate) {
Dart_Handle schedule_microtask;
if (is_ui_isolate) {
schedule_microtask =
InvokeFunction(builtin_library, "_getScheduleMicrotaskClosure");
} else {
Dart_Handle isolate_lib = Dart_LookupLibrary(ToDart("dart:isolate"));
Dart_Handle method_name =
Dart_NewStringFromCString("_getIsolateScheduleImmediateClosure");
schedule_microtask = Dart_Invoke(isolate_lib, method_name, 0, NULL);
}
Dart_Handle async_library = Dart_LookupLibrary(ToDart("dart:async"));
Dart_Handle set_schedule_microtask = ToDart("_setScheduleImmediateClosure");
Dart_Handle result = Dart_Invoke(async_library, set_schedule_microtask, 1,
&schedule_microtask);
PropagateIfError(result);
}
複製代碼
在這裏,給 set_schedule_microtask 傳的參數時 schedule_microtask,這個函數則是來自於名爲 _getIsolateScheduleImmediateClosure 的函數,且這就是一個 dart 函數,直接搜索,即可以在 sdk/lib/_internal/vm/lib/isolate_patch.dart 找到函數定義,this
void _isolateScheduleImmediate(void callback()) {
assert((_pendingImmediateCallback == null) ||
(_pendingImmediateCallback == callback));
_pendingImmediateCallback = callback;
}
@pragma("vm:entry-point", "call")
void _runPendingImmediateCallback() {
final callback = _pendingImmediateCallback;
if (callback != null) {
_pendingImmediateCallback = null;
callback();
}
}
/// The embedder can execute this function to get hold of
/// [_isolateScheduleImmediate] above.
@pragma("vm:entry-point", "call")
Function _getIsolateScheduleImmediateClosure() {
return _isolateScheduleImmediate;
}
複製代碼
從而得知,_ScheduleImmediate._closure 就是 _isolateScheduleImmediate,因此,callback(也就是 _startMicrotaskLoop)最後做爲 _isolateScheduleImmediate 的參數調用,也就是把它賦值給 _pendingImmediateCallback。spa
接着看 microtask 的執行,從上面得知,_pendingImmediateCallback 就是 _startMicrotaskLoop,並且 _pendingImmediateCallback 在 _runPendingImmediateCallback 函數中被調用,也就是說,當 _runPendingImmediateCallback 被調用時,便會啓動新一輪 microtask 的執行。
看到 _runPendingImmediateCallback 這個名字是否有點眼熟,以前在介紹 Future 的時候,經過查看代碼,瞭解到 dart vm 中處理消息事件,最終會調用 dart 中的 _handleMessage 函數,
@pragma("vm:entry-point", "call")
static void _handleMessage(Function handler, var message) {
// TODO(floitsch): this relies on the fact that any exception aborts the
// VM. Once we have non-fatal global exceptions we need to catch errors
// so that we can run the immediate callbacks.
handler(message);
_runPendingImmediateCallback();
}
複製代碼
在 dart 中,全部的代碼其實都是經過這裏調用的,不論是當咱們啓動一個 Isolate,仍是經過 Future 執行異步調用。好比當咱們直接啓動 dart 時,它會在 vm 中先建立好一個 isolate,而後執行它的 entry point,對於 root isolate,它的 entry point 就是 main,對於自定義的 isolate,它的 entry point 就是外部傳入的函數,而執行 entry point 的方式,就是經過事件隊列,能夠看下面這段代碼:
@pragma("vm:entry-point", "call")
void _startIsolate(
Function entryPoint, List<String>? args, Object? message, bool isSpawnUri) {
_delayEntrypointInvocation(entryPoint, args, message, isSpawnUri);
}
void _delayEntrypointInvocation(Function entryPoint, List<String>? args,
Object? message, bool allowZeroOneOrTwoArgs) {
final port = RawReceivePort();
port.handler = (_) {
port.close();
if (allowZeroOneOrTwoArgs) {
if (entryPoint is _BinaryFunction) {
(entryPoint as dynamic)(args, message);
} else if (entryPoint is _UnaryFunction) {
(entryPoint as dynamic)(args);
} else {
entryPoint();
}
} else {
entryPoint(message);
}
};
port.sendPort.send(null);
}
複製代碼
當 isolate 在 vm 中建立好後,c++ 就會調用 _startIsolate 啓動 isolate,而在這個函數中,它依舊是經過 ReceivePort/SendPort 向事件隊列中發送一個事件,以此來執行 entryPoint。
以上文字,僅爲說明 _handleMessage 這個函數的調用時機,同時也是在說明 _runPendingImmediateCallback 的調用時機,也就是 _startMicrotaskLoop。
// sdk/lib/async/schedule_microtask.dart
void _startMicrotaskLoop() {
_isInCallbackLoop = true;
try {
// Moved to separate function because try-finally prevents
// good optimization.
_microtaskLoop();
} finally {
_lastPriorityCallback = null;
_isInCallbackLoop = false;
if (_nextCallback != null) {
_AsyncRun._scheduleImmediate(_startMicrotaskLoop);
}
}
}
void _microtaskLoop() {
for (var entry = _nextCallback; entry != null; entry = _nextCallback) {
_lastPriorityCallback = null;
var next = entry.next;
_nextCallback = next;
if (next == null) _lastCallback = null;
(entry.callback)();
}
}
複製代碼
這個函數的實現仍是挺簡單的,就是直接遍歷 microtask 隊列去執行,有一點,當咱們在 micro task 執行過程當中再建立 microtask 的時候,因爲此時 _microtaskLoop 還未結束,因此當這個 microtask 執行完以後,會繼續執行新加的 microtask。不過在 _startMicrotaskLoop 中執行 _microtaskLoop 外面加了一個 try...finally 卻是沒太理解這裏的用途,由於從這裏直到 _handleMessage 都沒見到捕獲異常的操做。
總的來講,以上就是 microtask 從建立到執行的過程,下面具體講講幾種不一樣的代碼塊具體的執行時機。
從當前的信息瞭解到,在一個連續的代碼中,能夠有三種形式的代碼調用,
這三種形式的調用,其執行前後爲直接調用->microtask->事件隊列, 從以前的分析中就很容易理解了,下面再總結一下。
首先,直接調用很好理解,他們都是同在一個函數下,按順序調用。
而後,microtask 的調用是發生在當前的消息事件調用以後(從 _handleMessage 實現可知),而從以前的分析中得知,全部的關於 dart 代碼的調用,其入口都是 _handleMessage,直接執行 main 函數的時候是,新啓動一個 isolate 也是,而 _handleMessage 下有兩個入口,一個是 handler,一個是 _runPendingImmediateCallback,而無論 microtask 是在這兩個函數的哪個中建立的,都會在本次 _handleMessage 調用過程當中執行。
而第三種,消息事件調用就不同了,它必定是要等到下次甚至好幾回以後的 _handleMessage 中才會調用,相比之下,它的調用很昂貴,須要通過 dart vm 的一層處理才能調用到。
總結,基於 dart 的單線程事件循環模型,咱們能夠將 _handleMessage 看做一次事件循環,那麼「直接調用」與「microtask」都是在當次 _handleMessage 的調用中就會調用到,而「事件隊列調用」則至少要等到下次 _handleMessage 調用,由此決定了這三種代碼調用的執行順序。