Skip to content

Commit

Permalink
Break into chapters for Gitbook
Browse files Browse the repository at this point in the history
  • Loading branch information
kriskowal committed Oct 7, 2015
1 parent 7a7c3fa commit 3ad8651
Show file tree
Hide file tree
Showing 24 changed files with 1,831 additions and 1 deletion.
25 changes: 24 additions & 1 deletion SUMMARY.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,27 @@

# Summary

* [A General Theory of Reactivity](README.md)
* [Introduction](intro.md)
* [Concepts](concepts.md)
* Idioms
- [Iterators](iterators.md)
- [Generator Functions](generator-functions.md)
- [Generators](generators.md)
- [Async Values](async-values.md)
- [Async Functions](async-functions.md)
- [Async Queues](async-queues.md)
- [Semaphores](semaphores.md)
- [Async Buffers](async-buffers.md)
- [Async Iterators](async-iterators.md)
- [Remote Iterators](remote-iterators.md)
- [Async Generators](async-generators.md)
- [Async Generator Functions](async-generator-functions.md)
- [Observables](observables.md)
- [Observables and Signals](signals.md)
- [Behaviors](behaviors.md)
* Concrete Cases
- [Progress and ETA](progress.md)
* [Conclusion](conclusion.md)
* [Glossary](glossary.md)
* [Acknowledgements](acknowledgements.md)

6 changes: 6 additions & 0 deletions acknowledgements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

# Acknowledgements

I am grateful to Domenic Denicola, Ryan Paul, and Kevin Smith for reviewing and
providing feedback on various drafts of this article.

116 changes: 116 additions & 0 deletions async-buffers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@

# Promise Buffers

Consider another application.
You have a producer and a consumer, both doing work asynchronously, the producer
periodically sending a value to the consumer.
To ensure that the producer does not produce faster than the consumer can
consume, we put an object between them that regulates their flow rate: a buffer.
The buffer uses a promise queue to transport values from the producer to the
consumer, and another promise queue to communicate that the consumer is ready
for another value from the producer.
The following is a sketch to that effect.

```js
var outbound = new PromiseQueue();
var inbound = new PromiseQueue();
var buffer = {
out: {
next: function (value) {
outbound.put({
value: value,
done: false
});
return inbound.get();
},
return: function (value) {
outbound.put({
value: value,
done: true
})
return inbound.get();
},
throw: function (error) {
outbound.put(Promise.throw(error));
return inbound.get();
}
},
in: {
yield: function (value) {
inbound.put({
value: value,
done: false
})
return outbound.get();
},
return: function (value) {
inbound.put({
value: value,
done: true
})
return outbound.get();
},
throw: function (error) {
inbound.put(Promise.throw(error));
return outbound.get();
}
}
};
```

This sketch uses the vernacular of iterators and generators, but each of these
has equivalent nomenclature in the world of streams.

- `in.yield` means “write”.
- `in.return` means “close”.
- `in.throw` means “terminate prematurely with an error”.
- `out.next` means “read”.
- `out.throw` means “abort or cancel with an error”.
- `out.return` means “abort or cancel prematurely but without an error”.

So a buffer fits in the realm of reactive interfaces.
A buffer has an asynchronous iterator, which serves as the getter side.
It also has an asynchronous generator, which serves as the setter dual.
The buffer itself is akin to an asynchronous, plural value.
In addition to satisfying the requirements needed just to satisfy the
triangulation between synchronous iterables and asynchronous promises,
it solves the very real world need for streams that support pressure
to regulate the rate of flow and avoid over-commitment.
An asynchronous iterator is a readable stream.
An asynchronous generator is a writable stream.

| Stream | | | |
| ----------------- | ------- | -------- | ------------ |
| Promise Buffer | Value | Plural | Temporal |
| Promise Iterator | Getter | Plural | Temporal |
| Promise Generator | Setter | Plural | Temporal |

A buffer has a reader and writer, but there are implementations of reader and
writer that interface with the outside world, mostly files and sockets.

In the particular case of an object stream, if we treat `yield` and `next` as
synonyms, the input and output implementations are perfectly symmetric.
This allows a single constructor to serve as both reader and writer.
Also, standard promises use the [Revealing Constructor] pattern, exposing the
constructor for the getter side.
The standard hides the `Promise.defer()` constructor method behind the scenes,
only exposing the `resolver` as arguments to a setup function, and never
revealing the `{promise, resolver}` deferred object at all.
Similarly, we can hide the promise buffer constructor and reveal the input side
of a stream only as arguments to the output stream constructor.

```js
var reader = new Stream(function (write, close, abort) {
// ...
});
```

The analogous method to `Promise.defer()` might be `Stream.buffer()`, which
would return an `{in, out}` pair of entangled streams.

[Revealing Constructor]: http://domenic.me/2014/02/13/the-revealing-constructor-pattern/

See the accompanying sketch of a [stream][] implementation.

[stream]: http://kriskowal.github.io/gtor/docs/stream

95 changes: 95 additions & 0 deletions async-functions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@

# Asynchronous Functions

Generator functions have existed in other languages, like Python, for quite some
time, so folks have made some clever uses of them.
We can combine promises and generator functions to emulate asynchronous
functions.
The key insight is a single, concise method that decorates a generator, creating
an internal "promise trampoline".
An asynchronous function returns a promise for the eventual return value, or the
eventual thrown error, of the generator function.
However, the function may yield promises to wait for intermediate values on its
way to completion.
The trampoline takes advantage of the ability of an iterator to send a value
from `next` to `yield`.

```js
var authenticated = Promise.async(function *() {
var username = yield getUsernameFromConsole();
var user = getUserFromDatabase(username);
var password = getPasswordFromConsole();
[user, password] = yield Promise.all([user, password]);
if (hash(password) !== user.passwordHash) {
throw new Error("Can't authenticate because the password is invalid");
}
})
```

Mark Miller’s [implementation][Async] of the `async` decorator is succinct and
insightful.
We produce a wrapped function that will create a promise generator and proceed
immediately.
Each requested iteration has three possible outcomes: yield, return, or throw.
Yield waits for the given promise and resumes the generator with the eventual
value.
Return stops the trampoline and returns the value, all the way out to the
promise returned by the async function.
If you yield a promise that eventually throws an error, the async function
resumes the generator with that error, giving it a chance to recover.

[Async]: http://wiki.ecmascript.org/doku.php?id=strawman:async_functions#reference_implementation

```js
Promise.async = function async(generate) {
return function () {
function resume(verb, argument) {
var result;
try {
result = generator[verb](argument);
} catch (exception) {
return Promise.throw(exception);
}
if (result.done) {
return result.value;
} else {
return Promise.return(result.value).then(donext, dothrow);
}
}
var generator = generate.apply(this, arguments);
var donext = resume.bind(this, "next");
var dothrow = resume.bind(this, "throw");
return donext();
};
}
```

As much as JavaScript legitimizes the async promise generators by supporting
returning and throwing, now that Promises are part of the standard, the powers
that sit on the ECMAScript standards committee are contemplating special `async`
and `await` syntax for this case.
The syntax is inspired by the same feature of C#.

```js
var authenticate = async function () {
var username = await getUsernameFromConsole();
var user = getUserFromDatabase(username);
var password = getPasswordFromConsole();
[user, password] = await Promise.all([user, password]);
return hash(password) === user.passwordHash;
})
```

One compelling reason to support special syntax is that `await` may have higher
precedence than the `yield` keyword.

```js
async function addPromises(a, b) {
return await a + await b;
}
```

By decoupling **async functions** from **generator functions**, JavaScript opens
the door for **async generator functions**, foreshadowing a **plural** and
**temporal** getter, a standard form for readable streams.

Loading

0 comments on commit 3ad8651

Please sign in to comment.