Chapter 5. The Stream Framework

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:

  • Single-subscription streams versus broadcast streams
  • The stream framework API

Why you should use streams

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:

  • The code is cleaner, so you don't need to remember how to push data through the nonstreaming API
  • The file streams to clients in chunks, one chunk at a time as soon as they are received from the disk
  • The server doesn't need buffer chunks of a file in the memory when the remote clients are on a slow or high-latency connection, because the stream handling backs the pressure automatically

Streams make development simple and elegant.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.159.223