future
是代表可能還沒有發生的運算結果的物件。結果可能在未來的某個時間是可知的,因此被稱為“future”。
簡而言之,就是通過future
來實現非同步。正如isolate
與程式類似,future
也與執行緒類似。但也僅類似而已,畢竟future
中還是與執行緒有很大區別的,比如執行緒既可執行IO密集型任務,也可執行計算密集型任務,而future
僅能執行IO密集型任務。
在Flutter
中,Future
是一個抽象類。可以自己來實現,也可以使用預設的實現類_Future
,但基本上都是使用的預設實現。下面就來看_Future
的實現原理。
1、_Future的狀態
通過分析_Future
的實現程式碼,可以發現它存在以下五種狀態。
_stateIncomplete
:值為0,是初始狀態,等待返回結果。在該狀態下,_resultOrListeners
是節點為_FutureListener
物件的單連結串列的頭節點。_statePendingComplete
:值為1,等待完成。在呼叫_asyncComplete
、_asyncCompleteError
方法時設定。_resultOrListeners
是節點為_FutureListener
物件的單連結串列的頭節點。_stateChained
:值為2。在該狀態下,當前future
與另外一個future
連線在一起,另一個future
的返回結果也變成當前future
的的返回結果。當前future``的resultOrListeners
指向另一個future
。_stateValue
:值為4。該狀態下future
已經成功執行並帶有返回值。_stateError
:值為8。該狀態下future
已成功執行但返回了出錯資訊。
也就是無論如何使用_Future
,它都是在上面這些狀態間轉化。在這裡,我們不考慮出錯的情況,所以也就忽略_stateError
狀態,那麼就存在下面最基礎的幾種狀態轉化可能。
- _stateIncomplete->_stateValue,當直接呼叫
Future
的構造方法或者且其delayed
時出現的情況。 - _stateIncomplete->_statePendingComplete->_stateValue,當呼叫
Future
的value
方法時出現的情況。 - _stateIncomplete->_stateChained->_stateValue,當
Future
的返回值時一個Future
物件時出現的情況。
後面就根據上面三種情況來分析_Future
的實現原理。
2、_stateIncomplete->_stateValue
來看一個示例,如下。
//example1
Future(() {
print("f1");
return "f2";
}).then((value) => print("value:$value"));
//或,example2
Future.delayed(Duration(milliseconds: 1000), () {
print("f1");
return "f2";
}).then((value) => print("value:$value"));
//或,...
...
//print
flutter: f1
flutter: value:f2
複製程式碼
程式碼很簡單明瞭。由於example2與example1的實現原理完全相同,所以這裡以example1為例來分析其實現原理,實現程式碼如下。
abstract class Future<T> {
//建立一個_Future物件
factory Future(FutureOr<T> computation()) {
_Future<T> result = new _Future<T>();
Timer.run(() {
try {
//執行computation方法並把返回值傳給_Future的_complete方法
result._complete(computation());
} catch (e, s) {
_completeWithErrorCallback(result, e, s);
}
});
return result;
}
}
複製程式碼
程式碼很簡單,就是通過Timer
來實現方法的非同步執行並返回一個_Future
物件。關於Timer
是如何來實現非同步的,可以去閱讀Flutter之Timer原理解析這篇文章。
再來看_Future
的then
方法,它的程式碼實現如下。
class _Future<T> implements Future<T> {
var _resultOrListeners;
bool get _mayAddListener => _state <= _statePendingComplete;
//建立一個_FutureListener物件及_Future物件。但會把_FutureListener物件新增到連結串列中,_Future物件返回。
Future<R> then<R>(FutureOr<R> f(T value), {Function onError}) {
...
_Future<R> result = new _Future<R>();
_addListener(new _FutureListener<T, R>.then(result, f, onError));
return result;
}
//將listener新增到連結串列的具體實現
void _addListener(_FutureListener listener) {
if (_mayAddListener) {
//將listener新增到連結串列中
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) {
//將listener新增到source物件所在的連結串列中
source._addListener(listener);
return;
}
_cloneResult(source);
}
// Handle late listeners asynchronously.
_zone.scheduleMicrotask(() {
//處理呼叫then方法時傳遞的回撥方法。
_propagateToListeners(this, listener);
});
}
}
}
複製程式碼
在上面程式碼中,then
方法會建立一個新的_Future
物件——result
,然後把該物件及呼叫then
方法傳遞的回撥方法一起賦給_FutureListener
物件——listener
,最後再通過_addListener
方法來處理listener
物件並在then
方法中返回result
。
再來看_addListener
方法,它會根據當前_Future
物件的狀態來做不同的操作,主要有以下幾種可能。
- 當
_Future
的狀態值不大於_statePendingComplete
,會將listener
新增到連結串列中,等待非同步執行完畢後再來遍歷連結串列並執行該listener
。 - 當
_Future
的狀態值是_stateChained
時,並且當前_Future
指向的_Future
物件——source
的狀態值小於_stateValue
時(也就是source
的非同步執行未完成)。則將listener
新增到source
物件所存在的連結串列中。 - 當
_Future
的狀態值是_stateChained
時,並且當前_Future
指向的_Future
物件——source
的狀態值大於等於_stateValue
時(也就是source
的非同步執行已經完成)。則將source
的返回值拷貝給當前_Future
,並通過微任務來保證_propagateToListeners
方法的儘快執行。 - 當
_Future
的狀態值是大於_stateChained
時。則通過微任務來保證_propagateToListeners
方法的儘快執行。
在上面示例中,由於滿足狀態值不大於_statePendingComplete
。所以此時會把listener
作為頭節點新增到連結串列中,此時兩個_Future
的關係如下。
當非同步執行返回結果後,會呼叫_Future
的_complete
方法並把非同步執行的結果作為引數傳入。來看程式碼實現。
class _Future<T> implements Future<T> {
// This method is used by async/await.
//將_Future的狀態修改為_stateValue
void _setValue(T value) {
assert(!_isComplete); // But may have a completion pending.
_state = _stateValue;
_resultOrListeners = value;
}
//從連結串列中獲取尾節點
_FutureListener _removeListeners() {
// Reverse listeners before returning them, so the resulting list is in
// subscription order.
_FutureListener current = _resultOrListeners;
_resultOrListeners = null;
return _reverseListeners(current);
}
//連結串列反轉
_FutureListener _reverseListeners(_FutureListener listeners) {
_FutureListener prev;
_FutureListener current = listeners;
while (current != null) {
_FutureListener next = current._nextListener;
current._nextListener = prev;
prev = current;
current = next;
}
return prev;
}
void _complete(FutureOr<T> value) {
//判斷返回結果是否是Future或者其實現類
if (value is Future<T>) {
...
} else {
_FutureListener listeners = _removeListeners();
_setValue(value);
_propagateToListeners(this, listeners);
}
}
}
複製程式碼
由於示例中的返回結果不是Future
及其實現類,所以這時候就從通過_removeListeners
方法來從連結串列中獲取尾節點並執行_setValue
方法來修改當前_Future
物件的狀態,此時_Future
的狀態是_stateValue
。最後再呼叫_propagateToListeners
來遍歷連結串列並執行所有listener
。
下面在來看_propagateToListeners
方法的實現,它是_Future
中非常重要的一個方法,在後面的狀態轉換中最終呼叫的也是該方法。
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;
}
//通常情況下,futures僅有一個listener,如本示例。但如果有多個listener,將以遞迴的方式來處理所有listener。多listener的情況在後面會講述到。
while (listeners._nextListener != null) {
_FutureListener listener = listeners;
listeners = listener._nextListener;
listener._nextListener = null;
_propagateToListeners(source, listener);
}
//listener是當前_Future物件中連結串列的尾節點
_FutureListener listener = listeners;
final sourceResult = source._resultOrListeners;
//設定listenerHasError與listenerValueOrError的初始狀態
bool listenerHasError = hasError;
var listenerValueOrError = sourceResult;
//僅當遇到錯誤或者存在回撥方法時,才執行下面的程式碼。由於有許多futures不存在回撥方法,所以這些futures也就不執行下面的程式碼,這是一個重要的優化。
if (hasError || listener.handlesValue || listener.handlesComplete) {
...
// 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() {...}
//執行回撥方法
void handleValueCallback() {
try {
listenerValueOrError = listener.handleValue(sourceResult);
} catch (e, s) {
listenerValueOrError = new AsyncError(e, s);
listenerHasError = true;
}
}
//執行錯誤回撥
void handleError() {...}
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);
//如果listener的返回值是future,則將該future與當前_Future關聯起來並結束當前_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;
}
}
//獲取下一個即將執行的_Future物件
_Future result = listener.result;
//獲取_Future物件的listeners,這個listeners也是尾節點
listeners = result._removeListeners();
if (!listenerHasError) {
//將result的狀態修改為_stateValue
result._setValue(listenerValueOrError);
} else {
AsyncError asyncError = listenerValueOrError;
result._setErrorObject(asyncError);
}
// Prepare for next round.
source = result;
}
}
複製程式碼
_propagateToListeners
方法中程式碼比較多,但主要就是以遞迴的方式來遍歷_Future
中以_FutureListener
為節點的連結串列,並執行對應的回撥。如果正確執行,那麼回撥方法的呼叫是在handleValueCallback
中,如果出現異常則在handleError
中來執行錯誤回撥。
如果是上面的示例,那麼最終也是在_propagateToListeners
方法中呼叫_setValue
方法來將_Future
的狀態更新為_stateValue
。
下面再來看另外一種狀態轉換的情況。
2.2、_stateIncomplete->_statePendingComplete->_stateValue
來看一個示例,如下。
Future.value("f1").then((value) {
print("value:$value");
});
Future.value(() {
print("value:f1");
return "f2";
}).then((fun) {
print("value:${fun()}");
});
//print
flutter: value:f1
flutter: value:f1
flutter: value:f2
複製程式碼
本示例與上節的示例稍有不同,本示例中呼叫了Future
的value
方法。而這個方法又有什麼特點尼?下面就來看該方法的實現。
abstract class Future<T> {
//建立一個_Future物件
factory Future.value([FutureOr<T> value]) {
return new _Future<T>.immediate(value);
}
}
複製程式碼
在該方法就是簡單建立一個_Future
物件並呼叫該物件的immediate
方法,並沒有通過Timer
來實現非同步,那麼再來看immediate
方法的實現。
_Future.immediate(FutureOr<T> result) : _zone = Zone.current {
_asyncComplete(result);
}
void _setPendingComplete() {
_state = _statePendingComplete;
}
void _asyncComplete(FutureOr<T> value) {
//暫不考慮value是Future的情況
if (value is Future<T>) {
_chainFuture(value);
return;
}
_setPendingComplete();
//通過微任務來執行_completeWithValue方法
_zone.scheduleMicrotask(() {
_completeWithValue(value);
});
}
void _completeWithValue(T value) {
_FutureListener listeners = _removeListeners();
_setValue(value);
_propagateToListeners(this, listeners);
}
複製程式碼
由於不考慮value
是Future
的情況,所以在_asyncComplete
方法中,先通過_setPendingComplete
方法來將當前_Future
物件的狀態由_stateIncomplete
改為_setPendingComplete
,然後再通過微任務來實現非同步。
再來看then
方法,上節詳細講述過,這裡就不多說明了。由於在本例中是通過微任務來實現非同步的,所以在示例中就會先呼叫_Future
的then
方法。此時兩個_Future
之間的關係如下。
當開始執行_completeWithValue
方法時,則會取出圖中的listener
物件,然後將_Future1
的狀態由_setPendingComplete
改為_stateValue
,最終再通過_propagateToListeners
方法來處理listener
。
關於_propagateToListeners
方法在上節已有了詳細說明。
2.3、_stateIncomplete->_stateChained->_stateValue
仔細觀察前兩節的示例,會發現非同步執行的結果都是一個非Future
物件。那麼如果非同步執行結果是一個Future
物件時,其實現原理是怎樣的尼?
來看一個返回值是Future
物件的示例,如下。
Future.delayed(Duration(milliseconds: 500), () {//f1
print("f1");
return Future.delayed(Duration(milliseconds: 1000), () {//f2
print("f2");
return Future.delayed(Duration(milliseconds: 2000), () {//f3
print("f3");
return "f4";
}).then((value) {//f4
print("f5:$value");
});
}).then((value) {//f5
print("f6:$value");
});
}).then((value) {//f6
print("f7:$value");
return "f8";
}).then((value) {//f7
print("f9:$value");
});
//print
flutter: f1
flutter: f2
flutter: f3
flutter: f5:f4
flutter: f6:null
flutter: f7:null
flutter: f9:f8
複製程式碼
執行上面示例,可以發現f6
、f7
的輸出受f5
的返回值影響,f5
的輸出及返回值受f4
影響。也就是如果當前Future
物件的返回值是一個Future
物件,那麼當前Future
物件的後續執行則需要等待這個Future
物件執行完畢。下面來看實現原理。
當f1
在非同步執行時,會先呼叫f1
的then
方法,這時候f1
與通過f1
的then
方法建立的Future
物件關係如下。
當f1
非同步執行完畢後,會呼叫_Future
的_complete
方法,由於f1
的返回值是一個Future
物件,所以與返回值非_Future
物件的實現稍有不同。
class _Future<T> implements Future<T> {
void _complete(FutureOr<T> value) {
//判斷返回結果是否是Future或者其實現類
if (value is Future<T>) {
if (value is _Future<T>) {
//如果Future的具體實現類是_Future
_chainCoreFuture(value, this);
} else {
//如果Future的具體實現類是自定義類
_chainForeignFuture(value, this);
}
} else {...}
}
}
複製程式碼
這裡先暫不考慮自定義類實現Future
的情況,所以就來看_chainCoreFuture
方法的實現。
class _Future<T> implements Future<T> {
//source是f1非同步執行的返回值
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);
}
}
}
複製程式碼
由於此時f1
的狀態還是_stateIncomplete
,所以就直接來判斷source
的狀態。如果source
的狀態大於且等於_stateValue
,就直接把source
的返回值拷貝給f1
,並遍歷f1
的連結串列來執行對應的回撥方法;否則將f1
的狀態更新為_stateChained
並通過_prependListeners
方法將f1
中的listener
連結串列移動到source
中,source
中原有的連結串列新增到尾部即可。
class _Future<T> implements Future<T> {
void _setChained(_Future source) {
_state = _stateChained;
_resultOrListeners = source;
}
//
void _prependListeners(_FutureListener listeners) {
if (listeners == null) return;
if (_mayAddListener) {
//source中已存在的連結串列
_FutureListener existingListeners = _resultOrListeners;
_resultOrListeners = listeners;
if (existingListeners != null) {
_FutureListener cursor = listeners;
//遍歷從f1中移過來的連結串列
while (cursor._nextListener != null) {
cursor = cursor._nextListener;
}
//source中原有連結串列新增到新連結串列尾部
cursor._nextListener = existingListeners;
}
} else {...}
}
}
複製程式碼
此時f1
與f2
的關係如下。
f1
的_resultOrListeners
不再指向連結串列的首節點,而是指向f2
,而f2
中包含從f1
中移過來的連結串列及自身存在的連結串列。
以此類推,在等待f3
的非同步執行時,示例中各個Future
物件的關係如下。
然後當f3
非同步執行完畢後,f3
中連結串列會倒序執行。由於在執行listener
節點時,也是會執行listener
中result
指向的future
物件,而result
指向的future
物件中又可以存在連結串列及result
指向的一個新future
物件,以此類推,就是Future
的執行原理。當然在本示例中,result
指向的future
物件不存在連結串列。
細心一點可以發現,此時f1
、f2
還是_stateChained
狀態,並沒有轉變為_stateValue
狀態。但如果再次呼叫f1
、f2
的then
方法,就可以將f1
、f2
的狀態更新為_stateValue
了,這就是上面說的四種狀態轉換情況中的第三情況。
3、總結
上面就是Future
使用的最基礎幾種情況,雖然Future
的使用場景很多,但基本上都是上面幾種的各種組合。
最後再注意一點,如果_Future
的狀態是_stateValue
或者_stateError
。那麼此時候再呼叫該_Future
的then
方法,基本上就是通過微任務來執行回撥方法了。關於微任務的實現原理,後面再來詳細敘述。