more docs

This commit is contained in:
Joannis Orlandos 2017-09-07 14:37:04 +02:00
parent 056c3e6f12
commit 1df755f181
3 changed files with 135 additions and 11 deletions

View File

@ -1,8 +1,4 @@
# Async
Async provides the fundament for asynchronous libraries. It consists of Futures and Streams.
## Promise and Future
# Promise and Future
When working with asynchronous APIs, one of the problems you'll face is not knowing when a variable is set.
@ -29,7 +25,7 @@ You can imagine code becoming complex. Difficult to read and comprehend.
Promises and futures are two types that this library introduces to solve this.
### Creating a promise
## Creating a promise
Promises are important if you're implementing a function that returns a result in the future, such as the database shown above.
@ -69,7 +65,7 @@ func fetchUser(named name: String) -> Future<User> {
}
```
### On future completion
## On future completion
When a promise completes, you can chain the result/error into a closure:
@ -86,7 +82,7 @@ future.then { user in
}
```
### Catching specific errors
## Catching specific errors
Sometimes you only care for specific errors, for example, for logging.
@ -106,7 +102,7 @@ future.then { user in
}
```
### Mapping results
## Mapping results
Futures can be mapped to different results asynchronously.
@ -125,7 +121,7 @@ futureUsername.then { username in
}
```
### For synchronous APIs
## For synchronous APIs
Sometimes, an API needs to be used synchronously in a synchronous envinronment.

125
3.0/docs/async/stream.md Normal file
View File

@ -0,0 +1,125 @@
# Stream
Streams is a mechanism that you can implement on objects that process any information efficiently and asynchronously without bloat.
There are three primary stream protocols:
- InputStream
- OutputStream
- Stream
Conforming to Stream means conformance to both InputStream and OutputStream. So `Stream` is both processing output and providing output.
InputStream is a protocol that, when implemented, accepts streaming input. An example can be a TCP socket that, on input, writes data to the socket.
OutputStream is a protocol that, when implement, can emit output.
### Concept
In Vapor 3 (related libraries), almost everything is a stream. TCP Server is a stream of clients. Each client is a stream of received binary data. For HTTP, each client has an HTTP Request Parser, and Response Serializer. A parser accepts the binary stream and outputs a request stream. And a responder accepts a response and outputs a binary stream (that you can send back to the client's TCP socket as input for the binary stream).
## Implementing an example stream
This example is a stream that deserializes `ByteBuffer` to `String` streaming/asynchronously.
```swift
struct InvalidUTF8 : Error {}
// Deserializes `ByteBuffer` (`Input`) to `String` (`Output`) using the provided encoding
class StringDeserializationStream : Async.Stream {
typealias Input = ByteBuffer
typealias Output = String
// Used only by this specific stream to specify an encoding
let encoding: String.Encoding
// MARK: Stream requirements
// An error stream that can be listened to for errors in this stream
var errorStream: BaseStream.ErrorHandler?
// A handler that can be set to handle output
var outputStream: OutputHandler?
// Creates a new `StringDeserializationStream`
init(encoding: String.Encoding = .utf8) {
// Sets the String encoding
self.encoding = encoding
}
// Receives `Input`/`ByteBuffer` from another stream or manual call
//
// Attempts to process it to a String using the specified encoding
func inputStream(_ input: Input) {
// Converts the `Input`/`ByteBuffer` to a String
guard let string = String(bytes: input, encoding: self.encoding) {
// Stream an error if string initialization failed
self.errorStream?(InvalidUTF8())
return
}
// On success, output the created string
self.outputStream?(string)
}
}
```
## Chaining Streams
The above stream can now be called manually, but you still need to receive the output.
In this example, we'll take a TCP Stream implementation that outputs `ByteBuffer` for incoming data.
```swift
// TCP stream is a Stream of `ByteBuffer`
let tcpStream = ...
let stringStream = StringDeserializationStream()
// tcpStream's output equals stringStream's input, we can drain tcpStream into stringStream
//
// This will call `stringStream.inputStream` for every tcpStream's output
tcpStream.drain(into: stringStream)
// StringStream can then be drained to print the results
stringStream.drain { string in
// print the results
print(string)
}
```
## Transforming streams without an intermediate stream
The above stream `StringDeserializationStream` is a very simple example of implementing a stream.
Streams support two kinds of transforms. `flatMap` and `map`. Map transforms the output of the stream into a new stream with different output. And `flatMap` does the same, but allows returning `nil` and does not output it.
```swift
// `flatMap`s the data into a `String?`. If the string results in `nil`, the resulting `stringStream` does not get called.
// `stringStream` is a stream outputting `String`
let stringStream = tcpStream.flatMap { bytes in
return String(bytes: bytes, encoding: self.encoding)
}
// `map`s the data into a `String?`. If the string results in `nil`, the resulting `optionalStringStream` emits `nil`, too.
// `optionalStringStream` is a stream outputting `String?`
let optionalStringStream = tcpStream.flatMap { bytes in
return String(bytes: bytes, encoding: self.encoding)
}
```
As you see, you an provide a closure to do the mapping for you. If you want to reuse this code instead, you could make it a function for simplicity. This function can then be used instead of the closure.
```swift
// Creates a `String` from `ByteBuffer`. This can return `nil` if the `ByteBuffer` doesn't contain valid UTF-8
func utf8String(from bytes: ByteBuffer) -> String? {
return String(bytes: bytes, encoding: self.encoding)
}
// `flatMap`s the data into a `String?`. If the string results in `nil`, the resulting `stringStream` does not get called.
// `stringStream` is a stream outputting `String`
let stringStream = tcpStream.flatMap(utf8String)
// `map`s the data into a `String?`. If the string results in `nil`, the resulting `optionalStringStream` emits `nil`, too.
// `optionalStringStream` is a stream outputting `String?`
let optionalStringStream = tcpStream.flatMap(utf8String)
```

View File

@ -3,6 +3,9 @@ copyright: 'Copyright &copy; 2017 Qutheory, LLC'
pages:
- Overview: 'index.md'
- Async:
- 'Promise & Future': 'async/promise-future.md'
- 'Stream': 'async/stream.md'
markdown_extensions:
- admonition
@ -28,7 +31,7 @@ extra:
font:
text: 'Roboto Slab'
code: 'Source Code Pro'
google_analytics:
- 'UA-76177358-4'
- 'auto'