Future

A Future has three states unfinished, complete with a value, and complete with an exception. Using a Future simplifies event tasks. If you have a button that you click to download an image, first the event loop will process your click and then start downloading the image. When the download is complete, you can use then to register the callback and get the image and display it.

Usually we don’t create one directly, network download images will return a Future, file I/O will return a Future, so how do we create one? Only the keyword async is required to indicate that the function is executed asynchronously and the return type is Future

.

Future<String> getStr()async{
  var str = HttpRequest.getString('www.fgyong.cn');
  return str;
}
Copy the code

Get the data using the HTTP request address www.fgyong.cn and return it.

How do I receive text?

It’s as simple as using the await keyword to register the THEN callback.

main(List<String> args) async {
  String string = await getStr();
  print(string);
}
Copy the code

Is equal to:

main(List<String> args) async {
  getStr().then((value) {
    print(value);
  });
}
Copy the code

The former is officially recommended because it looks like a synchronous function, with fewer layers of nesting, making it easier for developers to understand the code. To delay the animation hide time, use future.delayed ().

await Future.delayed(Duration(seconds: 2), () {
hideAnimation();
});
Copy the code

If you already have a value and want to execute it asynchronously, you can use future.value ()

Stream

The DART: Async library contains two types that are important to many DART apis: Stream and Future. If a Future represents the result of a single computation, a stream is a series of results. You listen to the stream for notifications of results (data and errors) and the closure of the stream. You can also pause while listening to a stream or stop listening before the stream is complete.

How to use Stream

Streams can be created in a number of ways, more on this later, but they can all be used in the same way: asynchronous for loops (usually just called await for) iterate over the events of a stream, such as for loop iteration. Such as:

Future<int> sumStream(Stream<int> stream) async {
  var sum = 0;
  await for (var value in stream) {
    sum += value;
  }
  return sum;
}
Copy the code

This code simply takes each event in the stream of integer events, adds them up, and returns (and) their sum. When the body of the loop ends, the function is paused until the next event arrives or the stream completes. This function is marked with the async keyword, which is needed to use await for loops. The following example tests the previous code by using the async * function to generate a simple stream of integers:

import 'dart:async';

Future<int> sumStream(Stream<int> stream) async {
  var sum = 0;
  await for (var value in stream) {
    sum += value;
  }
  return sum;
}

Stream<int> countStream(int to) async* {
  for (int i = 1; i <= to; i++) {
    yield i;
  }
}

main() async {
  var stream = countStream(10);
  var sum = await sumStream(stream);
  print(sum); / / 55
}
Copy the code

When there are no more events in the flow, the flow is completed and the code that receives the event is notified as if it were a new event. When the event is read with await for loop, the loop stops after the flow completes. In some cases, an error occurs before the flow completes; There could be a failure in the network fetching a file from a remote server, or there could be an error in the code that creates the event, but someone needs to know about it. Streams can also pass error events, just like data events. Most streams will stop after the first error, but it is possible to pass multiple error streams and more data after the error event occurs. In this document, we only discuss streams that produce at most one error. The loop statement raises an error when reading a stream with await for. This also closes the loop. You can use try-catch to catch errors. The following example raises error 4 when the loop iterator is equal:

import 'dart:async';

Future<int> sumStream(Stream<int> stream) async {
  var sum = 0;
  try {
    await for (var value instream) { sum += value; }}catch (e) {
    return - 1;
  }
  return sum;
}

Stream<int> countStream(int to) async* {
  for (int i = 1; i <= to; i++) {
    if (i == 4) {
      throw new Exception('Intentional exception');
    } else {
      yield i;
    }
  }
}

main() async {
  var stream = countStream(10);
  var sum = await sumStream(stream);
  print(sum); // -1
}
Copy the code

Two kinds of Stream

The most common streams for a single subscription stream contain a series of events that are part of a larger whole. Events must be delivered in the correct order, and no events must be lost. This is the stream you get when you read a file or receive a Web request. Such streams can only be listened to once. Listening again later may mean missing the original event, and then the rest is meaningless. When you start listening, the data will be extracted and provided in blocks. Broadcast stream Another type of stream is for a single message that can be processed at once. For example, this flow can be used for mouse events in a browser. You can start listening to such a stream at any time, and events are triggered as you listen. Multiple listeners can listen simultaneously, and you can listen again later after canceling a previous subscription.

The method of the Stream

Future<T> get first;
Future<bool> get isEmpty;
Future<T> get last;
Future<int> get length;
Future<T> get single;
Future<bool> any(bool Function(T element) test);
Future<bool> contains(Object needle);
Future<E> drain<E>([E futureValue]);
Future<T> elementAt(int index);
Future<bool> every(bool Function(T element) test);
Future<T> firstWhere(bool Function(T element) test, {T Function() orElse});
Future<S> fold<S>(S initialValue, S Function(S previous, T element) combine);
Future forEach(void Function(T element) action);
Future<String> join([String separator = ""]);
Future<T> lastWhere(bool Function(T element) test, {T Function() orElse});
Future pipe(StreamConsumer<T> streamConsumer);
Future<T> reduce(T Function(T previous, T element) combine);
Future<T> singleWhere(bool Function(T element) test, {T Function() orElse});
Future<List<T>> toList();
Future<Set<T>> toSet();
Copy the code

All of these methods can basically loop with await for

Future<bool> contains(Object needle) async {
  await for (var event in this) {
    if (event == needle) return true;
  }
  return false;
}

Future forEach(void Function(T element) action) async {
  await for (var event in this) {
    action(event);
  }
}

Future<List<T>> toList() async {
  final result = <T>[];
  await this.forEach(result.add);
  return result;
}

Future<String> join([String separator = ""]) async= > (await this.toList()).join(separator);
Copy the code

Modify the Stream

Stream<R> cast<R>();
Stream<S> expand<S>(可迭代<S> Function(T element) convert);
Stream<S> map<S>(S Function(T event) convert);
Stream<T> skip(int count);
Stream<T> skipWhile(bool Function(T element) test);
Stream<T> take(int count);
Stream<T> takeWhile(bool Function(T element) test);
Stream<T> where(bool Function(T event) test);
Copy the code

All of this is basically using closures to filter the content of the stream, but of course you can transform the content.

Listen to the Stream

Finally, there is the listener, which is triggered when a stream changes. All streams can be listened on.

StreamSubscription<T> listen(void Function(T event) onData,
    {Function onError, void Function() onDone, bool cancelOnError});
Copy the code

Create your own Stream

There are roughly three ways to create a Stream, as follows:

  • conversionStram
  • useasync*createStream
  • useStreamControllercreate

Convert Stram

Often, some streams contain values that are not what we want, so we need to convert them, for example, from numbers to strings.

  Future<String> _toString() async {
    var s = await _stream().map((event) => event.toString()).join('|');
    return s;
  }
Copy the code

You can also convert it to an array

 Future<List> _toList() async {
    var s = await _stream().toList();
    return s;
  }
Copy the code

useasync*createStream

One way to create a new stream is to use an async * function. The flow is created when the function is called, and the body of the function starts running while listening for the flow. When the function returns, the stream is closed. Before the function returns, it can use yield or yield * statements to emit events on the stream.

Here’s a raw example that periodically emits numbers:

Stream<int> timedCounter(Duration interval, [int maxCount]) async* {
  int i = 0;
  while (true) {
    await Future.delayed(interval);
    yield i++;
    if (i == maxCount) break; }}Copy the code

This function returns a Stream. When you listen to the stream, the body starts running. It repeatedly delays the request interval and then produces the next number. If the count argument is ignored, there is no stop condition on the loop, so the stream always outputs larger and larger numbers – or until the listener unsubscribes. When the listener is cancelled (by calling cancel () on the StreamSubscription object returned by the LISTEN () method), the next time the principal reaches the yield statement, the yield acts as a return statement. Execute all closed finally blocks, and then the function exits. If the function tries to produce a value before exiting, the operation will fail and serve as the return value. When the function finally exits, the Future returned by the cancel () method completes. If the function exits with an error, the Future ends with that error; Otherwise, it ends with NULL. A more useful example is a function that converts a sequence:

Stream<T> streamFromFutures<T>(可迭代<Future<T>> futures) async* {
  for (var future in futures) {
    var result = await future;
    yieldresult; }}Copy the code

Using yield *

Use yield* to call another function to fetch the next value, and do not run when it is not fetched.

  Stream<int> _stream() async* {
    if (_count < 10) {
      yield _count++;
      await Future.delayed(Duration(seconds: 1));
      sleep(Duration(seconds: 1));
      yield* _getDataFromServer(); }}Copy the code

yield*andyieldThe difference is that the latter directly returns a fixed value, while the former returns a function. The former is mostly used for shunt, putting oneStream<T>Divided into otherStream<R>orStream<E>.

useStreamControllercreatestream

_streamController = StreamController();
/ / to monitor
_streamController.stream.listen((event) { })
/// Add data
_streamController.add('data');
Copy the code

StreamControlelr class diagram as shown below, they from Sink to StreamSink are only to the import interface, there is no direct inheritance relationship, in the abstract class according to the function decoupling, and finally integrated by the StreamController, StreamControlelr will be used to add StreamControlelr and streams will be listened for. Finally, the system will call zone.rununaryguarded () in the current Zone. The Zone is like a sandbox environment and is created when the APP starts.

The principle of

_SyncStreamController(asyncStreamController, asyncStreamController, asyncStreamController);

factory StreamController(
  {void onListen(),
  void onPause(),
  void onResume(),
  onCancel(),
  bool sync: false{})return sync
    ? new _SyncStreamController<T>(onListen, onPause, onResume, onCancel)
    : new _AsyncStreamController<T>(onListen, onPause, onResume, onCancel);
}
Copy the code

In _BufferingStreamSubscription class, finally realize the onDone and onError function, when they were in the class instantiation registered callback function.

void onData(voidhandleData(T event)) { handleData ?? = _nullDataHandler;/ / the default value
  _onData = _zone.registerUnaryCallback<dynamic, T>(handleData);
}
void onDone(voidhandleDone()) { handleDone ?? = _nullDoneHandler; _onDone = _zone.registerCallback(handleDone); }Copy the code

So when was the listener function added? After getting a stream is _ControllerStream(), the listener function is implemented in _StreamImpl extends Steam. The code looks like this:

StreamSubscription<T> listen(void onData(T data),
      {Function onError, void onDone(), bool cancelOnError}) {
    cancelOnError = identical(true, cancelOnError);
    StreamSubscription<T> subscription =
        _createSubscription(onData, onError, onDone, cancelOnError);
    _onListen(subscription);
    return subscription;
  }
  
/// Create a subscriber
StreamSubscription<T> _createSubscription(void onData(T data),
    Function onError, void onDone(), bool cancelOnError) {
  return new _BufferingStreamSubscription<T>(
      onData, onError, onDone, cancelOnError);
}
Copy the code

Finally listening is to perform _BufferingStreamSubscription _onData (void the HandleData event (T)), here is the use of the _zone. RegisterUnaryCallback to register the callback function, Otherwise, an error will be reported during the call.

void onData(voidhandleData(T event)) { handleData ?? = _nullDataHandler; _onData = _zone.registerUnaryCallback<dynamic, T>(handleData);
}
Copy the code

In the stream.add() function, _add() is executed

void _add(T data) {
  assert(! _isClosed);if (_isCanceled) return;
  if (_canFire) {
    _sendData(data);
  } else {
    _addPending(new_DelayedData<T>(data)); }}Copy the code

When the state is off, declare directly; when the state is off, return the operation; when data can be sent, execute _sendData(). This function is the key function to send data.

void _sendData(T data) {
  assert(! _isCanceled);assert(! _isPaused);assert(! _inCallback);bool wasInputPaused = _isInputPaused;
  _state |= _STATE_IN_CALLBACK;// Take the sixth place
  _zone.runUnaryGuarded(_onData, data);
  _state &= ~_STATE_IN_CALLBACK;// Discard the sixth position
  _checkState(wasInputPaused);
}
Copy the code

Final Zone _zone = zone.current; To obtain the system zones and try to keep the system system guarded (_onData, data), use the “_state&=~_STATE_IN_CALLBACK” method to keep the system status. Instead, use “&=~” to keep the system system guarded. If the value of _STATE_IN_CALLBACK is 32, then the lower sixth bit is 1, ~_STATE_IN_CALLBACK, and all but the sixth bit is 1.

The _checkState(wasInutPaused) is then used to determine that there is no state change between the execution of the callback.

The same is true when _sendDone() is executed. If not, the _onDone callback is executed.

 void _sendDone() {
    assert(! _isCanceled);assert(! _isPaused);assert(! _inCallback);void sendDone() {
      // If the subscription has been canceled while waiting for the cancel
      // future to finish we must not report the done event.
      if(! _waitsForCancel)return;
      _state |= (_STATE_CANCELED | _STATE_CLOSED | _STATE_IN_CALLBACK);
      _zone.runGuarded(_onDone);
      _state &= ~_STATE_IN_CALLBACK;
    }

    _cancel();
    _state |= _STATE_WAIT_FOR_CANCEL;
    if(_cancelFuture ! =null &&
        !identical(_cancelFuture, Future._nullFuture)) {
      _cancelFuture.whenComplete(sendDone);
    } else{ sendDone(); }}Copy the code

Event flows are used upfrontZoneTo register,addWhen usingZoneCall registered callback, broadcast is a circular call

Key functions:

/// Register callback
_zone.registerUnaryCallback<dynamic, T>(handleData)

/// Registers callbacks without arguments

_zone.registerCallback(R callback())

/// Performs a callback with parameters

_zone.runUnaryGuarded(_onData, data)
Copy the code

See the official source code for more APIS.

reference

  • The official library
  • A fun animation loading library
  • Demo Code collection github

The article summary

Dart asynchrony and multithreading

Insight into state management –ScopeModel

Learn more about Flutter management –Redux

Detailed explanation of Flutter (iii. In-depth understanding of state management –Provider)

4. Deeper understanding of state management –BLoC

A detailed explanation of Flutter

1, Learn about the Stream

7. Understand the principle of drawing deeply

Detailed explanation of Flutter

Project recommend

  • A cool loading animation library
  • Flutter100 + component usage examples
  • Flutter entry to advanced ebook

The public,