In this chapter, we will talk about streams. Streams have existed since the early days of UNIX. They have proven to be a dependable way to compose large systems out of small components, which does one thing well. Streams restrict the implementation of a surface area into a consistent interface that can be reused. You can plug the output of one stream as the input to another and use libraries that operate abstractly on streams to institute high-level flow control. Streams are an important component of small program design and have important abstractions that are worth considering. In this chapter, we will cover the following topics:
Just imagine that you need to provide a file on a server in response to a client's request. The following code will do this:
import 'dart:io'; main() { HttpServer .bind(InternetAddress.ANY_IP_V4, 8080) .then((server) { server.listen((HttpRequest request) { new File('data.txt').readAsString() .then((String contents) { request.response.write(contents); request.response.close(); }); }); }); }
In the preceding code, we read the entire file and buffered it into the memory of every request before sending the result to the clients. This code works perfectly for small files, but what will happen if the data.txt
file is very large? The program will consume a lot of memory because it serves a lot of users concurrently, especially on slow connections. One big disadvantage of this code is that we have to wait for an entire file to be buffered in memory before the content can be submitted to the clients.
The HttpServer
object listens for the HTTP request, which is a Stream. The HttpServer
object then generates an HttpRequest
object and adds it to the stream. The body of the request that is delivered by an HttpRequest
object is a stream of byte lists. An HttpRequest
object provides you with an access to the response
property associated with an HttpResponse
object. We will write the content of a file into the body of the HttpResponse
object. The fact that the HttpRequest
and HttpResponse
classes are streams means that we can write our example in a better way, as shown in the following code:
import 'dart:io'; main() { HttpServer .bind(InternetAddress.ANY_IP_V4, 8080) .then((server) { server.listen((HttpRequest request) { new File('data.txt') .openRead() .pipe(request.response); }); }); }
The openRead
method creates a new independent Stream
Instance of byte lists for the content of this file. The pipe
method binds this stream as the input of the provided stream consumer and listens for the data
, error
, or done
events. The preceding code has the following benefits:
3.133.159.223