-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Thinking in Effection page and reorganize page (#846)
* Adding draft of Thinking in Effection page * Started writing the roseta stone section * Added running to completion demo * Corrected the example image * Improved Every function exists fully * Added text for No Operation runs longer than it's parent * Minor text adjustments * Added Charle's comments * Grammar, spelling, and rhythm pass * Renamed file, removed block quote and image --------- Co-authored-by: Charles Lowell <[email protected]>
- Loading branch information
Showing
4 changed files
with
132 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
|
||
|
||
| Async/Await | Effection | | ||
| ---------------- | ----------------- | | ||
| `Promise` | `Operation` | | ||
| `new Promise()` | `action()` | | ||
| `await` | `yield*` | | ||
| `async function` | `function*` | | ||
| `AsyncIterable` | `Stream` | | ||
| `AsyncIterator` | `Subscription` | | ||
| `for await` | `for yield* each` | | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
When we say that Effection is "Structured Concurrency and Effects for Javascript" we mean three things: | ||
|
||
1. No operation runs longer than its parent. | ||
2. Every operation exits fully. | ||
3. It's just JavaScript, and except for the guarantees derived from (1) and (2), it should feel familiar in every other | ||
way. | ||
|
||
Developing a new intuition about how to leverage Structured Concurrency, while leaning on your existing intuition as a | ||
JavaScript developer will help you get the most out of Effection and have you attempting things that you would never | ||
have even dreamed before. | ||
|
||
## No operation runs longer than its parent. | ||
|
||
In JavaScript, developers rarely have to think about memory allocation because memory lifetime is bound to the scope of | ||
the function that it was allocated for. When a function is finished, its scope is torn down and all of the memory | ||
allocated to variables in function's scope are released safely. Binding memory management to scope gives developers the | ||
freedom to focus on their application instead worrying about leaking memory. | ||
|
||
Structured concurrency establishes the same relationship between scope and asynchrony. Every Effection operation is | ||
bound to the lifetime of its parent. Because of this, Effection automatically tears down | ||
child operations when the parent operation completes or is halted. Like with memory management, binding asynchrony to | ||
scope frees developers to focus on writing their applications instead of worrying about where and when to run | ||
asynchronous cleanup. | ||
|
||
The key to achieving this freedom is to make the following mental shift about the natural lifetime of an asynchronous | ||
operation. | ||
|
||
**before** | ||
|
||
> An asynchronous operation will run as long as it needs to | ||
**after** | ||
|
||
> An asynchronous operation runs only as long as it's needed. | ||
Effection provides this shift. Whenever an operation completes, none of its child operations are left | ||
around to pollute your runtime. | ||
|
||
## Every operation exits fully. | ||
|
||
We expect synchronous functions to run completely from start to finish. | ||
|
||
```js {5} showLineNumbers | ||
function main() { | ||
try { | ||
fn() | ||
} finally { | ||
// code here is GUARANTEED to run | ||
} | ||
} | ||
``` | ||
|
||
Knowing this makes code predictable. Developers can be confident that their functions will either return a result or | ||
throw an error. You can wrap a synchronous function in a `try/catch/finally` block to handle thrown errors. The | ||
`finally` block can be used to perform clean up after completion. | ||
|
||
However, the same guarantee is not provided for async functions. | ||
|
||
```js {5} showLineNumbers | ||
async function main() { | ||
try { | ||
await new Promise((resolve) => setTimeout(resolve, 100,000)); | ||
} finally { | ||
// code here is NOT GUARANTEED to run | ||
} | ||
} | ||
|
||
await main(); | ||
``` | ||
|
||
Once an async function begins execution, the code in its `finally{}` blocks may never get | ||
a chance to run, and as a result, it is difficult to write code that always cleans up after itself. | ||
|
||
This hard limitation of the JavaScript runtime is called the [Await Event Horizon][await-event-horizon], | ||
and developers experience its impact on daily basis. For example, the very common [EADDRINUSE][eaddrinuse-error] | ||
error is caused by a caller not being able to execute clean up when a Node.js process is stopped. | ||
|
||
By contrast, Effection _does_ provide this guarantee. | ||
|
||
```js {7} showLineNumbers | ||
import { main, action } from "effection"; | ||
|
||
await main(function*() { | ||
try { | ||
yield* action(function*(resolve) { setTimeout(resolve, 100,000) }); | ||
} finally { | ||
// code here is GUARANTEED to run | ||
} | ||
}); | ||
``` | ||
|
||
When executing Effection operations you can expect that | ||
they will run to completion; giving every operation an opportunity to clean up. At first glance, this | ||
might seem like a small detail but it's fundamental to writing composable code. | ||
|
||
## It's just JavaScript | ||
|
||
Effection is designed to provide Structured Concurrency guarantees using common JavaScript language constructs such as | ||
`let`, `const`, `if`, `for`, `while`, `switch` and `try/catch/finally`. Our goal is to allow JavaScript developers to | ||
leverage what they already know while gaining the guarantees of Structured Concurrency. You can use all of these | ||
constructs in an Effection function and they'll behave as you'd expect. | ||
|
||
However, one of the constructs we avoid in Effection is the syntax and semantics of `async/await`. | ||
This is because `async/await` is [not capable of modeling structured concurrency][await-event-horizon]. Instead, we make | ||
extensive use of [generator functions][generator-functions] which are a core feature of JavaScript and are supported by all browsers | ||
and JavaScript runtimes. | ||
|
||
Finally, we provide a handy Effection Rosetta Stone to show how _Async/Await_ concepts map into Effection APIs. | ||
|
||
[generator-functions]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function* | ||
[await-event-horizon]: https://frontside.com/blog/2023-12-11-await-event-horizon/ | ||
[delimited-continuation-repo]: https://github.com/thefrontside/continuation | ||
[eaddrinuse-error]: https://stackoverflow.com/questions/14790910/stop-all-instances-of-node-js-server |