Flutter 基於 dart 語言,dart 本身是一個單執行緒模型,Future 也是基於單執行緒的非同步機制,即基於事件迴圈實現的非同步,與多執行緒實現的非同步並不一樣,比較類似於 Android 中的 Handler 機制,而所謂的非同步,就是向事件迴圈中心傳送一條訊息,等待排程,在之後的某個時刻執行程式碼,但是這段程式碼還是在當前執行緒執行的,所以,如果使用 Future 執行耗時任務,它可能不會阻塞當前的 UI 流程,不過後續的一些 UI 操作還是會受到影響。 使用 Future 非同步執行程式碼需要四個步驟:
- 建立任務
- 傳送任務
- 執行任務
- 執行功能程式碼
接收到的最終的任務就是一個 callback,參見:
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;
}
複製程式碼
_complete 函式用於處理 computation 函式執行的結果,分為三種型別判斷:
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);
}
}
複製程式碼
當返回結果還是一個 Future 型別時,和當返回結果不是 Future 物件時有這兩種不同的處理邏輯,後者,也就是 else 語句中的邏輯,可以看到就是將返回值傳遞給 listener ,然後呼叫 _propagateToListeners 將結果分發給 listener 的 listeners,但前者,if 語句中的邏輯,告訴我們當上一個 Future 返回出一個 Future 時,不能直接將這個 Future 當作 listener 的 value 然後通知 listener ,這涉及到 Future 使用過程中的一個規則:Listener 的監聽結果不能是一個 Future ,所以就有了如下這種程式碼:
test4() {
print("start");
Future(() {
print("return future");
return Future(() {
print("return test4");
return "test4";
});
}).then((FutureOr<String> s) {
if (s is Future<String>) {
s.then((String ss) {
print(ss + "_");
});
} else {
print(s);
}
});
print("end");
}
複製程式碼
這段程式碼最終列印的是 test4 而不是 test4_ ,而這種情況出現的原因,就在於 _complete 函式中對 Future 物件和非 Future 物件的兩種不同的處理。對於非 Future 物件,將返回值通過 _setValue 傳遞給 result ,再分發給其 listener ,這是一個比較常規的過程。而對於 Future 物件也有兩種不同的處理,當返回值是一個 _Future 物件時,呼叫 _chainCoreFuture ,否則呼叫 _chainForeignFuture ,這兩個函式功能上大同小異,只不過對於 Flutter 預設實現的 _Future,有一些更“本土化”的處理,而對於其他 Future 的實現,則只能使用 Future 帶有的函式來實現同樣的功能,所謂的處理,就是 Future 不能作為 listener 的返回值出現,具體看程式碼:
static void _chainForeignFuture(Future source, _Future target) {
assert(!target._isComplete);
assert(source is! _Future);
// Mark the target as chained (and as such half-completed).
target._setPendingComplete();
try {
source.then((value) {
assert(target._isPendingComplete);
// The "value" may be another future if the foreign future
// implementation is mis-behaving,
// so use _complete instead of _completeWithValue.
target._clearPendingComplete(); // Clear this first, it's set again.
target._complete(value);
},
// TODO(floitsch): eventually we would like to make this non-optional
// and dependent on the listeners of the target future. If none of
// the target future's listeners want to have the stack trace we don't
// need a trace.
onError: (error, [StackTrace stackTrace]) {
assert(target._isPendingComplete);
target._completeError(error, stackTrace);
});
} catch (e, s) {
// This only happens if the `then` call threw synchronously when given
// valid arguments.
// That requires a non-conforming implementation of the Future interface,
// which should, hopefully, never happen.
scheduleMicrotask(() {
target._completeError(e, s);
});
}
}
複製程式碼
source 是上一個 Future 的返回值,target 是 listener 對應的 Future ,它沒有將 source 設定為 target 的 value,而是繼續呼叫 source 的 then 函式,得到 source 的返回值後再呼叫 target 的 _complete 函式處理返回值,這裡便會陷入迴圈,如果 source 巢狀返回了多少個 Future ,這裡就需要呼叫多少次,總之,直到 source 返回了一個常規的值,才會結束這種迴圈,這就是 _chainForeignFuture 中所做的處理,只使用到了 Future 的 then 函式,看起來也比較容易理解,不過 _chainCoreFuture 看著就沒有這麼和藹了。
static void _chainCoreFuture(_Future source, _Future target) {
assert(target._mayAddListener); // Not completed, not already chained.
while (source._isChained) {
source = source._chainSource;
}
if (source._isComplete) {
_FutureListener listeners = target._removeListeners();
target._cloneResult(source);
_propagateToListeners(target, listeners);
} else {
_FutureListener listeners = target._resultOrListeners;
target._setChained(source);
source._prependListeners(listeners);
}
}
複製程式碼
首先判斷 source 是否處於 Future 鏈中,如果是,則找到鏈尾的 Future ,然後將這個 Future 作為 source ,如果 source 已經完成了,把 source 的返回值複製過來,然後分發給 listeners,如果沒有,則加入這個 Future 鏈,並將自己的 listeners 都移植到 source 身上。 那麼 Future 鏈是什麼?鏈尾的 source 又是什麼?為什麼 target 的 listeners 能夠直接交給 source ?歡迎來到。。。。 先看幾個示例程式碼:
test5() {
Future<String> f = Future.delayed(Duration(milliseconds: 100), () {
print("return test5");
return "test5";
});
Future(() {
print("return f");
return f;
}).then((String s) => print(s));
}
test6() {
Future<String> f = Future.delayed(Duration(milliseconds: 100), () {
print("return test6");
return "test6";
});
Future<String> ff = Future(() {
print("return f");
return f;
});
Future<String> fff = Future(() {
print("return ff");
return ff;
});
Future(() {
print("return fff");
return fff;
}).then((String s) => print(s));
}
複製程式碼
先看 test5 ,首先宣告瞭一個 Future f,延遲 100 ms,接著又定義了一個 Future 會返回 f,它有一個 listener 需要接受 String 引數。當第二個 Future 返回 f 時 f 還未執行完畢,於是此時執行的程式碼段就是:
_FutureListener listeners = target._resultOrListeners;
target._setChained(source);
source._prependListeners(listeners);
複製程式碼
即 Future 加入了 Future 鏈,並將 listner 移交給了 f,所以此時實際上的邏輯應該是這樣的:
test5() {
Future<String> f = Future.delayed(Duration(milliseconds: 100), () {
print("return test5");
return "test5";
});
f.then((String s) => print(s));
}
複製程式碼
於是一個雙層巢狀的 Future ,變成了一個沒有巢狀的 Future,當 f 執行完了之後,就會通知 listner 列印 s。 而對於 test6 ,這是一個多層巢狀,ff、fff 以及最後一個 Future ,返回的都是 Future ,那麼當 ff 執行完時,它返回了一個尚未執行完的 f,所以它會加入到 Future 鏈,此時鏈為 ff -> f,然後 fff 返回了 ff,而此時 ff 已經在 Future 鏈中,所以 fff 也會加入 Future 鏈,此時 fff -> ff -> f,然後就是 Future -> fff -> ff -> f,並且此時 Future 的 listener 掛在了 f 身上,於是也可以化簡為:
test6() {
Future<String> f = Future.delayed(Duration(milliseconds: 100), () {
print("return test6");
return "test6";
});
f.then((String s) => print(s));
}
複製程式碼
最終,當 f 執行完了之後,會進入 _complete 函式執行如下程式碼:
_FutureListener listeners = _removeListeners();
_setValue(value);
_propagateToListeners(this, listeners);
複製程式碼
此時的 listeners ,便是包含了 ff,fff 和 Future 這三個 Future 的 listener 集合,雖然如此,但此刻它們都已經移植到了 f 身上,並且這個移植是無縫的,從根本上來說,他們的 listener 實際上監聽的也正是 f 的返回值。 看到這裡,上面幾個問題應該有了解答,Future 鏈是一串 Future ,但是它們中只有一個 Future 返回了真實的資料,也就是鏈尾的那一個,前面的 Future 的 listeners 則是直接或間接地依賴著鏈尾 Future 的返回結果,所以它們的 listeners 也都直接移植到了鏈尾 Future 上,這個鏈尾的 Future ,就是 _chainCoreFuture 中的 source,而對於其它那些沒有返回真正資料的 Future ,是不會呼叫它們的 _propagateToListeners 函式的。 _propagateToListeners 負責將返回值傳遞給 listeners :
static void _propagateToListeners(_Future source, _FutureListener listeners) {
while (true) {
assert(source._isComplete);
bool hasError = source._hasError;
if (listeners == null) {
if (hasError) {
AsyncError asyncError = source._error;
source._zone
.handleUncaughtError(asyncError.error, asyncError.stackTrace);
}
return;
}
// Usually futures only have one listener. If they have several, we
// call handle them separately in recursive calls, continuing
// here only when there is only one listener left.
while (listeners._nextListener != null) {
_FutureListener listener = listeners;
listeners = listener._nextListener;
listener._nextListener = null;
_propagateToListeners(source, listener);
}
_FutureListener listener = listeners;
final sourceResult = source._resultOrListeners;
// Do the actual propagation.
// Set initial state of listenerHasError and listenerValueOrError. These
// variables are updated with the outcome of potential callbacks.
// Non-error results, including futures, are stored in
// listenerValueOrError and listenerHasError is set to false. Errors
// are stored in listenerValueOrError as an [AsyncError] and
// listenerHasError is set to true.
bool listenerHasError = hasError;
var listenerValueOrError = sourceResult;
// Only if we either have an error or callbacks, go into this, somewhat
// expensive, branch. Here we'll enter/leave the zone. Many futures
// don't have callbacks, so this is a significant optimization.
if (hasError || listener.handlesValue || listener.handlesComplete) {
Zone zone = listener._zone;
if (hasError && !source._zone.inSameErrorZone(zone)) {
// Don’t cross zone boundaries with errors.
AsyncError asyncError = source._error;
source._zone
.handleUncaughtError(asyncError.error, asyncError.stackTrace);
return;
}
Zone oldZone;
if (!identical(Zone.current, zone)) {
// Change zone if it's not current.
oldZone = Zone._enter(zone);
}
// These callbacks are abstracted to isolate the try/catch blocks
// from the rest of the code to work around a V8 glass jaw.
void handleWhenCompleteCallback() {
// The whenComplete-handler is not combined with normal value/error
// handling. This means at most one handleX method is called per
// listener.
assert(!listener.handlesValue);
assert(!listener.handlesError);
var completeResult;
try {
completeResult = listener.handleWhenComplete();
} catch (e, s) {
if (hasError && identical(source._error.error, e)) {
listenerValueOrError = source._error;
} else {
listenerValueOrError = new AsyncError(e, s);
}
listenerHasError = true;
return;
}
if (completeResult is Future) {
if (completeResult is _Future && completeResult._isComplete) {
if (completeResult._hasError) {
listenerValueOrError = completeResult._error;
listenerHasError = true;
}
// Otherwise use the existing result of source.
return;
}
// We have to wait for the completeResult future to complete
// before knowing if it’s an error or we should use the result
// of source.
var originalSource = source;
listenerValueOrError = completeResult.then((_) => originalSource);
listenerHasError = false;
}
}
void handleValueCallback() {
try {
listenerValueOrError = listener.handleValue(sourceResult);
} catch (e, s) {
listenerValueOrError = new AsyncError(e, s);
listenerHasError = true;
}
}
void handleError() {
try {
AsyncError asyncError = source._error;
if (listener.matchesErrorTest(asyncError) &&
listener.hasErrorCallback) {
listenerValueOrError = listener.handleError(asyncError);
listenerHasError = false;
}
} catch (e, s) {
if (identical(source._error.error, e)) {
listenerValueOrError = source._error;
} else {
listenerValueOrError = new AsyncError(e, s);
}
listenerHasError = true;
}
}
if (listener.handlesComplete) {
handleWhenCompleteCallback();
} else if (!hasError) {
if (listener.handlesValue) {
handleValueCallback();
}
} else {
if (listener.handlesError) {
handleError();
}
}
// If we changed zone, oldZone will not be null.
if (oldZone != null) Zone._leave(oldZone);
// If the listener’s value is a future we need to chain it. Note that
// this can only happen if there is a callback.
if (listenerValueOrError is Future) {
Future chainSource = listenerValueOrError;
// Shortcut if the chain-source is already completed. Just continue
// the loop.
_Future result = listener.result;
if (chainSource is _Future) {
if (chainSource._isComplete) {
listeners = result._removeListeners();
result._cloneResult(chainSource);
source = chainSource;
continue;
} else {
_chainCoreFuture(chainSource, result);
}
} else {
_chainForeignFuture(chainSource, result);
}
return;
}
}
_Future result = listener.result;
listeners = result._removeListeners();
if (!listenerHasError) {
result._setValue(listenerValueOrError);
} else {
AsyncError asyncError = listenerValueOrError;
result._setErrorObject(asyncError);
}
// Prepare for next round.
source = result;
}
}
複製程式碼
這個函式相對來說比較重量級,其完成的功能也是比較多的,不過結構還是比較清晰的,換算下來就是:
static void _propagateToListeners(_Future source, _FutureListener listeners) {
while (true) {
// 利用遞迴,將 source 的結果分發給 listeners 中的每一個 listener
// 如果 listener 具有這三個之一的能力,就去根據情況執行其中一個
if (hasError || listener.handlesValue || listener.handlesComplete) {
// 宣告 handleWhenCompleteCallback 函式
// 宣告 handleValueCallback 函式
// 宣告 handleError 函式
// 根據 listener 的能力及 source 的執行結果,選擇上面的其中一個函式執行
// 如果 listener 的返回結果還是一個 Future ,那就呼叫 _chainCoreFuture 或 _chainForeignFuture 進行處理
}
// 如果返回的是一個常規值,則將 value 設定給 result ,接著將 result 的結果分發給 result 的 listeners,所以給 source 和 listeners 重新賦值,while 迴圈
}
}
複製程式碼
脈絡就是這樣。這個函式裡面又出現了兩種分支,即 listener 處理之後的結果是否是一個 Future ,可以看如下兩個示例:
test7() {
print("start");
Future<String> f = Future.delayed(Duration(milliseconds: 100), () {
print("return test7");
return "test7";
});
Future<String> ff = Future(() {
print("return f");
return f;
});
Future(() {
print("return ff");
return ff;
}).then((String s) {
return Future.delayed(Duration(milliseconds: 100), () {
print("return s");
return s;
});
}).then((String s) => print(s));
print("end");
}
test8() {
print("start");
Future.delayed(Duration(milliseconds: 100), () {
print("return test8");
return "test8";
}).then((String s) {
print("return s1");
return s;
}).then((String s) {
print("return s2");
return s;
}).then((String s) {
print("return s3");
return s;
}).then((String s) {
print(s);
});
print("end");
}
複製程式碼
在 test7 中,Future 的返回值是 ff ,從上面對 _complete 函式的分析得知,test7 可以做如下轉換:
test7() {
print("start");
Future<String> f = Future.delayed(Duration(milliseconds: 100), () {
print("return test7");
return "test7";
});
f.then((String s) {
return Future.delayed(Duration(milliseconds: 100), () {
print("return s");
return s;
});
}).then((String s) => print(s));
print("end");
}
複製程式碼
那麼當 f 執行完畢的時候,便會通過 _propagateToListeners 將返回結果傳遞給 listener ,接著執行 listener 的 callback,但是 listener 的返回結果還是一個 Future ,則會執行這一段程式碼:
if (listenerValueOrError is Future) {
Future chainSource = listenerValueOrError;
// Shortcut if the chain-source is already completed. Just continue
// the loop.
_Future result = listener.result;
if (chainSource is _Future) {
if (chainSource._isComplete) {
listeners = result._removeListeners();
result._cloneResult(chainSource);
source = chainSource;
continue;
} else {
_chainCoreFuture(chainSource, result);
}
} else {
_chainForeignFuture(chainSource, result);
}
return;
}
複製程式碼
所以,test7 又會變成如下這樣:
test7() {
print("start");
Future<String> f = Future.delayed(Duration(milliseconds: 100), () {
print("return test7");
return "test7";
});
f.then((String s) {
Future f = Future.delayed(Duration(milliseconds: 100), () {
print("return s");
return s;
});
f.then((String s) => print(s));
return f;
});
print("end");
}
複製程式碼
至此,當 f 中的 Future 也執行完畢的時候,就會出發最後的 listener ,列印 s。 而對於 test8 ,多個 then 連線在一起,且返回的結果都是常規值,則對應著這段程式碼:
_Future result = listener.result;
listeners = result._removeListeners();
if (!listenerHasError) {
result._setValue(listenerValueOrError);
} else {
AsyncError asyncError = listenerValueOrError;
result._setErrorObject(asyncError);
}
// Prepare for next round.
source = result;
複製程式碼
result 即是 listener 對應的 Future,listeners 是 result 的 listeners,所以,每當一個 while 迴圈執行之後,就意味著一個 then 函式的結束,直到所有的 listener 都得到處理。 再來看下 then 函式:
Future<R> then<R>(FutureOr<R> f(T value), {Function onError}) {
Zone currentZone = Zone.current;
if (!identical(currentZone, _rootZone)) {
f = currentZone.registerUnaryCallback<FutureOr<R>, T>(f);
if (onError != null) {
// In checked mode, this checks that onError is assignable to one of:
// dynamic Function(Object)
// dynamic Function(Object, StackTrace)
onError = _registerErrorHandler(onError, currentZone);
}
}
_Future<R> result = new _Future<R>();
_addListener(new _FutureListener<T, R>.then(result, f, onError));
return result;
}
複製程式碼
_addListener 函式可以看出,Future 的 listener 就是在這個函式中新增的,listener 對應的 Future ,也是在這裡建立的,result、callback、onError 將會被構造成 _FutureListener 例項,_addListener 負責將 listener 新增到 Future 上。
void _addListener(_FutureListener listener) {
assert(listener._nextListener == null);
if (_mayAddListener) {
listener._nextListener = _resultOrListeners;
_resultOrListeners = listener;
} else {
if (_isChained) {
// Delegate listeners to chained source future.
// If the source is complete, instead copy its values and
// drop the chaining.
_Future source = _chainSource;
if (!source._isComplete) {
source._addListener(listener);
return;
}
_cloneResult(source);
}
assert(_isComplete);
// Handle late listeners asynchronously.
_zone.scheduleMicrotask(() {
_propagateToListeners(this, listener);
});
}
}
複製程式碼
一般情況下,listener 會被新增到 Future 的 listeners 中,當 Future 已經是 Future 鏈中的一員時,listener 則會直接被新增到 source 中去,或者當 Future 已經執行完成時,則直接呼叫 _propagateToListeners 處理。 再來說說 Future 中常用的一些引數,比如 _isChained 、_isComplete 以及 _mayAddListener 這些到底是怎麼確定的?_chainSource 、_resultOrListeners 這些變數都是些什麼? 總的來說,這些都指向兩個變數,_state 和 _resultOrListeners,比如:
bool get _mayComplete => _state == _stateIncomplete;
bool get _isPendingComplete => _state == _statePendingComplete;
bool get _mayAddListener => _state <= _statePendingComplete;
bool get _isChained => _state == _stateChained;
bool get _isComplete => _state >= _stateValue;
bool get _hasError => _state == _stateError;
複製程式碼
又比如:
AsyncError get _error {
assert(_hasError);
return _resultOrListeners;
}
_Future get _chainSource {
assert(_isChained);
return _resultOrListeners;
}
_FutureListener _removeListeners() {
// Reverse listeners before returning them, so the resulting list is in
// subscription order.
assert(!_isComplete);
_FutureListener current = _resultOrListeners;
_resultOrListeners = null;
return _reverseListeners(current);
}
void _setChained(_Future source) {
assert(_mayAddListener);
_state = _stateChained;
_resultOrListeners = source;
}
void _setValue(T value) {
assert(!_isComplete); // But may have a completion pending.
_state = _stateValue;
_resultOrListeners = value;
}
void _setErrorObject(AsyncError error) {
assert(!_isComplete); // But may have a completion pending.
_state = _stateError;
_resultOrListeners = error;
}
void _cloneResult(_Future source) {
assert(!_isComplete);
assert(source._isComplete);
_state = source._state;
_resultOrListeners = source._resultOrListeners;
}
複製程式碼
所以,error 、result、chainSource、listeners 實際上都是同一個變數表示的,只不過在不同的狀態下,_resultOrListeners 有著不同的含義,在 _hasError 狀態下,_resultOrListeners 表示 error,在 !_isComplete 狀態下,_resultOrListeners 表示 listeners,而在給 _resultOrListeners 設定值的時候,一般也會一併給 _state 賦值。