Flutter之Future原理解析

大逗大人發表於2020-06-22

future是代表可能還沒有發生的運算結果的物件。結果可能在未來的某個時間是可知的,因此被稱為“future”。

簡而言之,就是通過future來實現非同步。正如isolate與程式類似,future也與執行緒類似。但也僅類似而已,畢竟future中還是與執行緒有很大區別的,比如執行緒既可執行IO密集型任務,也可執行計算密集型任務,而future僅能執行IO密集型任務。

Flutter中,Future是一個抽象類。可以自己來實現,也可以使用預設的實現類_Future,但基本上都是使用的預設實現。下面就來看_Future的實現原理。

1、_Future的狀態

通過分析_Future的實現程式碼,可以發現它存在以下五種狀態。

  1. _stateIncomplete:值為0,是初始狀態,等待返回結果。在該狀態下,_resultOrListeners是節點為_FutureListener物件的單連結串列的頭節點。
  2. _statePendingComplete:值為1,等待完成。在呼叫_asyncComplete_asyncCompleteError方法時設定。_resultOrListeners是節點為_FutureListener物件的單連結串列的頭節點。
  3. _stateChained:值為2。在該狀態下,當前future與另外一個future連線在一起,另一個future的返回結果也變成當前future的的返回結果。當前future``的resultOrListeners指向另一個future
  4. _stateValue:值為4。該狀態下future已經成功執行並帶有返回值。
  5. _stateError:值為8。該狀態下future已成功執行但返回了出錯資訊。

也就是無論如何使用_Future,它都是在上面這些狀態間轉化。在這裡,我們不考慮出錯的情況,所以也就忽略_stateError狀態,那麼就存在下面最基礎的幾種狀態轉化可能。

  1. _stateIncomplete->_stateValue,當直接呼叫Future的構造方法或者且其delayed時出現的情況。
  2. _stateIncomplete->_statePendingComplete->_stateValue,當呼叫Futurevalue方法時出現的情況。
  3. _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原理解析這篇文章。

再來看_Futurethen方法,它的程式碼實現如下。

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物件的狀態來做不同的操作,主要有以下幾種可能。

  1. _Future的狀態值不大於_statePendingComplete,會將listener新增到連結串列中,等待非同步執行完畢後再來遍歷連結串列並執行該listener
  2. _Future的狀態值是_stateChained時,並且當前_Future指向的_Future物件——source的狀態值小於_stateValue時(也就是source的非同步執行未完成)。則將listener新增到source物件所存在的連結串列中。
  3. _Future的狀態值是_stateChained時,並且當前_Future指向的_Future物件——source的狀態值大於等於_stateValue時(也就是source的非同步執行已經完成)。則將source的返回值拷貝給當前_Future,並通過微任務來保證_propagateToListeners方法的儘快執行。
  4. _Future的狀態值是大於_stateChained時。則通過微任務來保證_propagateToListeners方法的儘快執行。

在上面示例中,由於滿足狀態值不大於_statePendingComplete。所以此時會把listener作為頭節點新增到連結串列中,此時兩個_Future的關係如下。

Flutter之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
複製程式碼

本示例與上節的示例稍有不同,本示例中呼叫了Futurevalue方法。而這個方法又有什麼特點尼?下面就來看該方法的實現。

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);
  }
複製程式碼

由於不考慮valueFuture的情況,所以在_asyncComplete方法中,先通過_setPendingComplete方法來將當前_Future物件的狀態由_stateIncomplete改為_setPendingComplete,然後再通過微任務來實現非同步。

再來看then方法,上節詳細講述過,這裡就不多說明了。由於在本例中是通過微任務來實現非同步的,所以在示例中就會先呼叫_Futurethen方法。此時兩個_Future之間的關係如下。

Flutter之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
複製程式碼

執行上面示例,可以發現f6f7的輸出受f5的返回值影響,f5的輸出及返回值受f4影響。也就是如果當前Future物件的返回值是一個Future物件,那麼當前Future物件的後續執行則需要等待這個Future物件執行完畢。下面來看實現原理。

f1在非同步執行時,會先呼叫f1then方法,這時候f1與通過f1then方法建立的Future物件關係如下。

Flutter之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 {...}
  }
}
複製程式碼

此時f1f2的關係如下。

Flutter之Future原理解析
在圖中,f1_resultOrListeners不再指向連結串列的首節點,而是指向f2,而f2中包含從f1中移過來的連結串列及自身存在的連結串列。

以此類推,在等待f3的非同步執行時,示例中各個Future物件的關係如下。

Flutter之Future原理解析

然後當f3非同步執行完畢後,f3中連結串列會倒序執行。由於在執行listener節點時,也是會執行listenerresult指向的future物件,而result指向的future物件中又可以存在連結串列及result指向的一個新future物件,以此類推,就是Future的執行原理。當然在本示例中,result指向的future物件不存在連結串列。

細心一點可以發現,此時f1f2還是_stateChained狀態,並沒有轉變為_stateValue狀態。但如果再次呼叫f1f2then方法,就可以將f1f2的狀態更新為_stateValue了,這就是上面說的四種狀態轉換情況中的第三情況。

3、總結

上面就是Future使用的最基礎幾種情況,雖然Future的使用場景很多,但基本上都是上面幾種的各種組合。

最後再注意一點,如果_Future的狀態是_stateValue或者_stateError。那麼此時候再呼叫該_Futurethen方法,基本上就是通過微任務來執行回撥方法了。關於微任務的實現原理,後面再來詳細敘述。

相關文章