Thinking in Effection
There are two fundamental ideas that will help you get the most of Effection.
- Structured Concurrency
- It's just JavaScript
Structured Concurrency
One way to think about Structured Concurrency is that it offers developers the same guarantees for their asyncronous code as they expect from syncronous code. These guarantees are that a child function will never outlive it's parent and every function runs to completion. When we write synchrous code in JavaScript we take these guarantees for granted which makes our code easier to write and think about. Effection is designed to give you these guarantees with as little friction as possible.
Child function will never outlive it's parent
In syncronous code, when you call a function from another function, you know that the child function will complete before the parent proceeds.
For example, the following code will output before child
, child
and after child
every time. This order is guaranteed by the JavaScript runtime.
function child() {
console.log("child");
}
function parent() {
console.log("before child");
child();
console.log("after child");
}
parent();
JavaScript runtime provides no predictable or reliable guarantees on what happens if the child
function calls an asynchronous operation. For example,
if we wrap the console.log('child')
in setTimeout
for 1 millisecond. The result will be before child
, after child
and child
which means that
the child function was still executing while the parent function was finished.
function child() {
setTimeout(() => console.log("child"), 1);
}
This happens because the JavaScript runtime does not guarantee the child function will never outline it's parent. Effection brings this Structured Concurrency guarantee to the JavaScript runtime environment. The same example implemented in Effection behaves according to the guarantees of Structure Concurrency.
import { sleep, run } from "effection";
function* child() {
yield* sleep(1);
console.log("child");
}
function* parent() {
console.log("before child");
yield* child();
console.log("after child");
}
await run(parent);
The above example will output before child
, child
and after child
as we would expect.
💡 You might assume that Effection makes everything asyncronous which is incorrect. Effection is built on Deliminated Continuation which allows us to treat syncronous and asyncronous code in the same way without making synchronous code asynchronous.
Every function runs to completion
We expect synchronous functions to run to completion. This guarantee is used by the JavaScript runtime to perform garbage collection by releasing any memory allocated during execution of a function.
For example, a function that defines a variable will leave no trace of that variable once that function is finished. Once fn()
is finished executing we take it for granted that any resources consumed by that function are released.
function fn() {
const value = 5;
}
fn();
JavaScript runtime does not offer the same guarantees for asynchronous code. In JavaScript, you may never know if an asyncronous process is truely finished.
// TODO: ADD EXAMPLE HERE
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 benefits of Structured Concurrency. This means that you can use
Async Rosetta Stone
Async | Effection |
---|---|
Promise | Operation |
new Promise() | action() |
await | yield* |
async function | function* |
AsyncIterable | Stream |
AsyncIterator | Subscription |
for await | for yield* each |