Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Rosetta Stone Guide #858

Merged
merged 4 commits into from
Dec 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion www/docs/actions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ async function fetch(url) {
}
```

Consulting the [Async Rosetta Stone](), we can substitute the async
Consulting the [Async Rosetta Stone](async-rosetta-stone), we can substitute the async
constructs for their Effection counterparts to arrive at a line for line
translation.

Expand Down
206 changes: 203 additions & 3 deletions www/docs/async-rosetta-stone.mdx
Original file line number Diff line number Diff line change
@@ -1,12 +1,212 @@
When we say "Effection is Structured Concurrency and Effects for
JavaScript", we mean "JavaScript" seriously. You shouldn't have to
learn an entirely new way of programming just to achieve structured
concurrency. That's why the Effection APIs mirror ordinary JavaScript
APIs so closely. That way, if you know how to do it in JavaScript, you
know how to do it in Effection.

The congruence between vanilla JavaScript constructs and their Effection
counterparts is reflected in the “Async Rosetta Stone.”

| Async/Await | Effection |
| ---------------- | ----------------- |
| `Promise` | `Operation` |
| `new Promise()` | `action()` |
| `await` | `yield*` |
| `async function` | `function*` |
| `Promise` | `Operation` |
| `new Promise()` | `action()` |
| `for await` | `for yield* each` |
| `AsyncIterable` | `Stream` |
| `AsyncIterator` | `Subscription` |
| `for await` | `for yield* each` |

## `await` \<=> `yield*`

Pause a computation and resume it when the value represented by the right hand
side becomes available.

Continue once a promise has settled:

```javascript
await promise;
```

Continue when operation is complete.

```js
yield* operation;
```

## `async function` \<=> `function*`

Compose a set of computations together with logic defined by JavaScript syntax:

Count down from 5 to 1 with an async function:

```js
async function countdown() {
for (let i = 5; i > 1; i--) {
console.log(`${i}`);
await sleep(1000);
}
console.log('blastoff!');
}
```

Count down from 5 to 1 with a generator function:

```js
import { sleep } from 'effection';

function* countdown() {
for (let i = 5; i > 1; i--) {
console.log(`${i}`);
yield* sleep(1000);
}
console.log('blastoff!');
}
```

Both will print:

```
5
4
3
2
1
blastoff!
```

To call an async function within an operation use [`call()`][call]:

```js
import { call } from 'effection';

yield* call(async function() {
return "hello world";
});
```

To run an operation from an async function use [`run()`][run] or [`Scope.run`][scope-run]:

```js
import { run } from 'effection';

await run(function*() {
return "hello world";
});
```

## `Promise` \<=> `Operation`

The `Promise` type serves roughly the same purpose as the `Operation`. It is a
abstract value that you can use to pause a computation, and resume when the
value has been computed.

To use a promise:

```js
let promise = await promise;
```

To use an operation:

```js
let operation = yield* operation;
```

To convert from a promise to an operation, use [`call()`][call]

```js
import { call } from 'effection';

let operation = call(promise);
```

to convert from an operation to a promise, use [`run()`][run] or [`Scope.run`][scope-run]

```js
import { run } from 'effection';

let promise = run(operation);
```

## `new Promise()` \<=> `action()`

Construct a reference to a computation that can be resolved with a callback.
In the case of `Promise()` the value will resolve in the next tick of the run
loop.

Create a promise that resolves in ten seconds:

```js
new Promise((resolve) => { setTimeout(resolve, 10000) });
```

Create an Operation that resolves in ten seconds:

```js
import { action } from 'effection';

action(function*(resolve) { setTimeout(resolve, 10000) });
```

A key difference is that the promise body will be executing immediately, but the
action body is only executed when the action is evaluated. Also, it is executed
anew every time the action is evaluated.

## `for await` \<=> `for yield* each`

Loop over an AsyncIterable with `for await`:

```js
for await (let item of iterable) {
//item logic
}
```

Loop over a `Stream` with `for yield* each`

```js
import { each } from 'effection';

for (let item of yield* each(stream)) {
// item logic
yield* each.next();
}
```

See the definition of [`each()`][each] for more detail.

## `AsyncIterable` \<=> `Stream`

A recipe for instantiating a sequence of items that can arrive over time. It is not
the sequence itself, just how to create it.

Use an `AsyncIterable` to create an `AsyncIterator`:

```js
let iterator = asyncIterable[Symbol.asyncIterator]();
```

Use a `Stream` as an operation to create a `Subscription`:

```js
let subscription = yield* stream;
```

To convert an `AsyncIterable` to a `Stream` use [`stream()`][stream]

```js
import { stream } from 'effection';

let itemStream = stream(asyncIterable);
```

## `AsyncIterator` \<=> `Subscription`

A stateful sequence of items that can be evaluated one at a time.

[call]: https://deno.land/x/effection/mod.ts?s=call
[run]: https://deno.land/x/effection/mod.ts?s=run
[scope-run]: https://deno.land/x/effection/mod.ts?s=Scope#method_run_0
[each]: https://deno.land/x/effection/mod.ts?s=each
Loading