Effection Logo

Thinking in Effection

When we say that Effection is "Structured Concurrency and Effects for Javascript" we mean three things:

  1. No function runs longer than its parent.
  2. Every function 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 function runs longer than its parent.

When you call a syncronous 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 function. For example, if we wrap the console.log('child') in setTimeout for 1 millisecond. The result will be before child, after child and child because child function was still executing after the parent function finished.

function child() {
  setTimeout(() => console.log("child"), 1);
}

This happens because the JavaScript runtime does not guarantee the no function runs longer than its 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 exists fully.

We expect synchronous functions to run completely from start to finish. This guarantee provided by the JavaScript runtime makes synchronous functions easier to write, understand and optimize.

For developers: This complete execution guarantee makes code predictable. Developers can be confident that their functions will either successfully return a result or throw an error. In case of errors, wrapping the function in a try/catch/finally will provide an opportunity to handle the error. The finally block can be used to perform clean up after completion.

For the JavaScript runtime: This guarantee is critical for memory management. The JavaScript runtime relies on this guarantee to release memory that was allocated to variables within a function to prevent holding unnecessary memory.

This critical guarantee that's provided for syncronous functions by the JavaScript runtime is not provided for asyncronous functions. The JavaScript runtime doesn't provide a way to interrupt the execution of the asyncronous function while allowing the the caller to complete. This limitation of the JavaScript runtime is described in greater detail in the Await Event Horizon in JavaScript blog post.

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 all of these constructs in an Effection function and they'll behave as you'd expect.

The one area where Effection can not provide Structured Concurrency guarantees is in runtime behaviour of async/await. We explained why in The Await Event Horizon in JavaScript blog post. Instead of async/await we use Generator Functions. Generator Functions are supported by all browsers and JavaScript runtimes.

Async Rosetta Stone

AsyncEffection
PromiseOperation
new Promise()action()
awaityield*
async functionfunction*
AsyncIterableStream
AsyncIteratorSubscription
for awaitfor yield* each
  • PreviousTypeScript