×

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: 

  1. Uncompleted 
  2. Completed with a value
  3. Completed with an error 
Dart Future And Stream

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 : 

  1. Throws = The Future returned is completed with an error. 
  2. Future itself = The Future returned is completed first, and then the completion of the created Future takes place. 
  3. Non – Future = The returned Future is completed with the value returned. 

Implementation =  factory Future( FutureOr <T> computation( ) ) ;

Properties 

  1. 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 : 

  1. 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. 
  2. 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 :

  1. Data event which is also known as the element of the stream.
  2. 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 

  1. Single – subscription stream
  2. 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( )


Related Topics

Dart Basics

Dart, as earlier mentioned multiple times, is very similar to C, C++ and Java. It is an object-oriented, garbage collecting and class-based programming language. Let’s look further and see what Dart has...

10 minutes read.

Dart Important Concepts

1. Anything placed in a variable is an object, and an object is an instance of a class. Numbers, functions, and null are all objects in Dart, and all these...

3 minutes read.

Dart If statement

If statement is used to set the control on the lines of code. Using if statement, block of code is executed only if the expression in the statement returns true....

1 minute read.

Unit Testing in Dart

Typing testing ensures that your app keeps working as you add more features or change existing functionality. Unit testing helps to confirm the behavior of a single function, method, or...

4 minutes read.

Dart Single-Page Application Architecture

In a single page application architecture, the source code for a single web page loads the entire application. The responsibility of building the user interface and requesting data from the...

2 minutes read.

Dart Concurrency

Dart concurrency allows us to run multiple or multiple components of a system at once. It issues several commands at once. Dart provides Isolates as a tool for performing compliance...

2 minutes read.

Dart Break and Continue

Loop statements are used to change the normal sequence of flow of the program. Dart supports two types of loop control statements: Break StatementContinue Statement Break Statement : The ‘ break ’ statement is...

4 minutes read.

Rust vs Dart

Rust is a system-level programming language that stands next to C ++ in terms of syntax, but offers greater speed and memory security. Dart, on the other hand, is an...

2 minutes read.

Dart Strings

Strings data type in Dart A Dart string is a sequence of characters with an encoding of UTF-16. The text associated with string data type is enclosed withing single (‘  ’)...

3 minutes read.

Dart Standard Input & Output

Standard Input in Dart (stdin): The standard input stream reads data both synchronously and asynchronously from the keyboard.  In Dart programming language, .readLineSync( ) function is used to accept input from the...

3 minutes read.

Callable Classes in Dart

Just like the functions, we can also call the instances of classes in Dart. Such classes are also known as “callable ” classes. We need to use the “call( )”...

3 minutes read.

Dart Enumerations

Enumeration data type in Dart In simple terms, Enumerations are referred to as named constant values. Constant values are declared as enumerations using the ‘enum’ keyword.  Implementation =  enum enum_name {    ...

2 minutes read.

Dart Assignment Operator

Assignment operators are the operators that assign value to the variables. The value on the right-hand side is assigned to the variable on the left-hand side. We can also use...

4 minutes read.

Dart super keyword

The ' super ' keyword is used to refer to the immediate parent class object of the currently in-use child class. Using this keyword, we can invoke the superclass (...

6 minutes read.

Dart Type inference

The task of interpreting the data types of fields, methods, local variables and most generic type arguments is handled by the static analyser. However, when not enough information is provided...

5 minutes read.

Dart If-else-if statement

If - else - if statement enables to check the set of test expressions that evaluate to Boolean True or False, and based on this true or false the particular...

2 minutes read.

Dart Installation Guide

There are various ways of compiling and running an application created in Dart, either by compiling the Dart code to JavaScript using the Dart2js tool or by running on the...

3 minutes read.

Dart Booleans

Booleans Dart has a data type named ‘bool’ to represent Boolean values : true and false, which are compile-time constants. ‘True’ or ‘False’ cannot be assigned with values 1 or 0. Example, bool...

1 minute read.

Golang vs Dart

Go is the procedural programming language. It was founded in 2007 by Robert Griesemer, Rob Pike, and Ken Thompson at Google but was launched in 2009 as the language of...

2 minutes read.

Dart Classes

Dart is an object – oriented programming language that supports all the object-oriented programming concepts such as classes, objects, inheritance, data abstraction and data encapsulation. A class can be defined as...

8 minutes read.