Dart Future and Stream
Before understanding Future and Stream in Dart, we need first to get familiar with two terms : Synchronous and Asynchronous Programming.
In synchronous programming, a single operation is handled at a time, and the processing proceeds to the next operation only when the first one is completed. While in asynchronous programming, we can take up another task even if the previous one is not finished.
Dart supports Asynchronous programming with the help of ‘Future’ and ‘Stream’ classes.
Future refers to the processing that is left incomplete in-between. An asynchronous function returns Future that tells when the result will be ready.
Stream refers to the sequence of asynchronous events which keep you informed of the next events in line.
Future
Future represents the value or error that is supposed to be available in the Future. Handling such errors or values at the time they would be available, is mandatory and should be taken care of.
Implementation = Future <data_type> class_name
They can basically exist in three states:
- Uncompleted
- Completed with a value
- Completed with an error
Every Dart code that we write, a single thread is dedicated to picking events from the event queue and processing them for the program's execution.
Future Constructors
A Future constructor is a constructor that takes a function as the parameter and returns some value which can be Future or non - Future. In simple words, it creates a Future that contains the result of the process that calls that Future.
The result can be one of the following options :
- Throws = The Future returned is completed with an error.
- Future itself = The Future returned is completed first, and then the completion of the created Future takes place.
- Non – Future = The returned Future is completed with the value returned.
Implementation = factory Future( FutureOr <T> computation( ) ) ;
Properties
- HashCode =
Returns the hash code for a numerical value, and it returns the same value for int and doubles when the value provided is the same.
Implementation = int get hashchode ;
- Runtime Type =
Represents the type of runtime an object has.
Implementation = Type get runtimeType ;
Methods
- asStream( ) =
This method creates a stream that contains the result of the completion of the specified Future. If a Future remains incomplete, the stream would never be able to produce any events. While, if the Future is complete, the stream executes the event and eventually closes down.
Implementation = Stream<T> asStream( ) ;
- catchError( ) =
This method is used to handle all the errors that the Future emits. It returns a new completed Future with some result which could be of the Future or the call back ' onError '. The Future can complete with the following cases :
- With a value = The Future returned is completed with this same value.
- With an error = Test is called with the error value. In this case, the completion of Future depends on the Boolean value of the test :
- True = The error calls the ' onError ', and with the result of this call, Future is executed.
- False = The exception is now not handled by ' catchError ' and the Future is made to complete with the same error.
- Omitted = The default value is true, and the function is supposed to always return true if the test is omitted.
Implementation =
Future<T> catchError (
Function onError ,
{ bool test (
Object error
) ? }
)
- then< R > =
This method registers all the callbacks that have to be made as soon as the Future is completed. As we know by now, a future can complete with a value or an error; let us then understand how the method works in each case :
- Completes with a value = The ' onValue ' callback will be called with the value with which the Future is completed. Depending upon the time of Future's completion, the callback is scheduled. If it is already completed, a callback won't be made immediately but would schedule for later.
- Completes with an error = The ' onError ' callback will be called with the error with which the Future is completed. Remember that the ' onError ' callback must return a value or Future to complete the returned Future with either of these two.
Finally, it returns a Future that is completed with the results of the call to
‘ onValue ' or ' onError '. If the callback returns a Future, completion of Future returned by ' then ' would be completed with the same result as of the returned Future. Else if the callback throws, the returned Future is completed with the thrown error.
If ' onError ' is not given, and this Future completes with an error, the error is forwarded directly to the returned Future.
Implementation =
Future<R> then<R> (
FutureOr<R> onValue (
T value
) ,
{ Function? onError }
) ;
- toString( ) =
Converts a given number into an equivalent shortest string and returns the same.
Implementation = external String toString( ) ;
- whenComplete( ) =
This method registers a function to be called when this Future completes. Irrespective of whether a Future completes with a value or an error, the action function is called. Asynchronously, this is equivalent to the " finally " block.
When this call is invoked, it returns a Future that would be completed in the same way as it was completed, unless an error occurs. If in case, the call does not return a Future, the value returned is ignored. And if it throws, the Future completes with that thrown error.
Let us say if the call returns another Future, then the Future returned previously is left in between until the completion of this another future. If this Future completes with an error, then the previous Future also completes with the same error. Remember that the value, as a result, is ignored.
Implementation =
Future<T> whenComplete (
FutureOr<void> action (
) ;
Stream
Stream is a way by which we receive a sequence of events. These events can correspond to :
- Data event which is also known as the element of the stream.
- Error event which intimates if the event has failed.
When the stream is done with emitting all the events, it notifies a single
' done ' event which signifies the end. We can use the listen( ) method with the function as a parameter to subscribe to the stream.
We receive these events by an active object of the stream called as StreamSubscription object. Using this object, we can either entirely terminate the notification update of the event or temporarily pause it.
Types of Streams
- Single – subscription stream
- Broadcast stream
Single-subscription Stream
This stream enables only a single listener to receive the event. It starts emitting events only when the listener is active and stops generating them as soon as the listener unsubscribes.
Remember that two listeners aren't allowed on a single-subscription stream even though the first one is cancelled.
Broadcast Stream
This stream enables any number of listeners and notifies the events as soon as they are ready, irrespective of whether the listeners are available or not. They are mainly used for independent events.
asBroadcastStream is used to enable several listeners to listen to a single subscription stream.
By default, the streams are single-subscription streams. In the case of listening to a stream twice, the compiler throws an exception.
Stream Constructors
- Stream<T> constructor =
This constructor is used to create a stream.
Implementation = Stream( ) ;
- Stream.empty( ) =
This constructor is used to create an empty broadcast stream, and it performs a single task of sending ‘ done ’ events when the stream is listened to.
Implementation = const factory Stream.empty( ) = _EmptyStream<T> ;
- Stream.error( ) =
This constructor is used to create a stream that emits a single error before completing. Then it stackTrace and completes with a ‘ done ’ event.
Implementation =
Stream< T >.error (
Object error,
[ StackTrace? stackTrace ]
)
Stream Properties
- First =
This property returns the first element of the given stream. As soon as the first element is retrieved, listening to this stream is stopped, by implicitly unsubscribing the method.
There can be two cases of its working :
- The stream is empty = In this case, the Future returned by the stream completes with an error.
- Occurrence of some error event = In this case, the Future returned by the stream completes with that error.
Implementation = Future<T> get first { }
- hashCode =
Returns the hash code for a numerical value, and it returns the same value for int and doubles when the value provided is the same.
Implementation = int get hashchode ;
- isEmpty =
This property checks whether the given stream contains any elements or not and accordingly returns true or false.
- True = When the stream ends without emitting any elements, indicating that the stream is empty.
- False = When the stream emits an element.
If the event causes an error, the future returned is completed with that error.
Implementation = Future<bool> get isEmpty { }
- Last =
This property returns the last element of the stream. In case of any error caused by the stream, the future returned is completed with that error. And if the stream is empty, the future returned completes with an error.
Implementation = Future<T> get last { }
- Length =
This property calculates the number of elements in the stream and returns the same. At the end of this stream, the future returned is completed with the number of elements calculated by this property.
If, in any case, the stream causes an error, the future returned is completed with that error, terminating the processing.
Implementation = Future<int> get length { }
- runtimeType =
Represents the type of runtime an object has.
Implementation = Type get runtimeType ;
- single =
This property returns the single element of the given stream. If, in any case, the stream causes an error, the future returned is completed with that error, terminating the processing.
If the stream is empty or has more than a single element, the future returned is completed with an error.
Implementation = Future<T> get single { }
- isBroadcast =
This property checks whether this stream is a broadcast stream or not and accordingly returns true or false.
Implementation = bool get isBroadcast ;
Methods
- any( ) =
This method checks whether ' test ' accepts any element provided by the stream. Here, we call ' test ' on each and every element of the stream to check the same. If it returns true, the future returned is completed with true. Else if the stream returns false, the future returned is completed with false.
If, in any case, the stream causes an error or the call to test throws, the future returned is completed with that error.
Implementation =
Future<bool> any (
bool test (
T element
)
)
- asBroadcastStream( ) =
This method returns a multi-subscription stream that produces the event as same as this. The stream returned will subscribe to this given stream.
Implementation =
Stream<T> asBroadcastStream (
{ void onListen (
StreamSubscription<T> subscription
) ? ,
void onCancel (
StreamSubscription<T> subscription
) ? }
)
- elementAt =
This method returns the value at the given index in the given stream. Listening to the stream is terminated as soon as the value at that index is retrieved.
If, in any case, the stream causes an error before the value is retrieved, the Future returned is completed with the error.
Implementation =
Future<T> elementAt (
int index
)
- handleError( )
This method creates a wrapper Stream that determines some errors from this given stream. This generated wrapper stream sends the error to the ' test ' to match whether the errors are the same, and then the ' onError ' function determines this error.
Implementation =
Stream<T> handleError (
Function onError ,
{ bool test (
dynamic error
) ? }
)
- listen( )
This method is used to subscribe the elements to the stream and then returns the StreamSubscription that handles the events emitted by the stream using the following handlers :
- onData
- onError
- onDone
Implementation =
StreamSubscription<T> listen (
void onData (
T event
) ? ,
{ Function? onError ,
void onDone (
) ? ,
bool? cancelOnError }
)
- toString( )
This method converts a given number into an equivalent shortest string and returns the same.
Implementation = external String toString( ) ;
- toList( )
This method collects all the elements of the given stream in the ' List '. It creates the List< T > and adds all the elements of the given stream to the list. At the end of the stream, the future returned is completed with the list.
Implementation = Future< List<T> > toList( )