diff --git a/www/docs/faq.mdx b/www/docs/faq.mdx new file mode 100644 index 00000000..3cbce1ff --- /dev/null +++ b/www/docs/faq.mdx @@ -0,0 +1,47 @@ +This page covers questions users of Effection ask as they go through the journey +of learning Effection. + +## Why can't I `yield*` to a promise? + +In TypeScript, if you tried to `yield* Promise.resolve(42)`, TypeScript compiler will +throw an error `Type 'Promise' must have a '[Symbol.iterator]()' method that returns an iterator.`. +In JavaScript, it will just not work. This happens because neither the TypeScript nor the JavaScript runtimes know how to +convert a Promise into an iterator, which is required for yield* to work. + +While adding the integration to make `yield*` work with a promise is possible by monkey patching the `Promise` prototype, we opted not to do so. Not only is monkey patching a global object from a library considered bad form, but it would also compromise the guarantees that are described in [Thinking in Effection](./thinking-in-effection) +by blurring the line about where those guarantees apply. Right now, the line is very clear: anything on the +right side of `yield*` is guaranteed to exit fully and never outlive the operation where the `yield*` was +called. We can't make these guarantees with Promises for the reasons described in [The Await Event Horizon](https://frontside.com/blog/2023-12-11-await-event-horizon/) +blog post. + +## Can I make Promise compatible with `yield*`? + +Yes. You can `yield*` to a `Promise` by monkey patching the `Promise` prototype. "Monkey patching" in JavaScript refers to the practice +of modifying or extending existing objects or functions at runtime, typically without changing their +original source code. It's considered risky because it breaks the expectations that developers maintaining +and running the code have of the JavaScript runtime. There are circumstances where the convenience of `yield*` +to promise outweighs the risks. We'll let you make the decision where those circumstances are for you. + +You can use the following code to monkeypatch Promise if you choose to do so. You can put this code into a module +and import it when you want this effect. + +```typescript +import { call, type Operation } from "effection"; + +declare global { + interface Promise extends Operation {} +} + +Object.defineProperty(Promise.prototype, Symbol.iterator, { + value() { + return call(() => this)[Symbol.iterator](); + }, +}); +``` + +## Why can't I use `call(Promise.resolve(42))` anymore? + +In Effection v3, it was possible to `yield* call(Promise.resolve(42))` but we removed this in Effection v4 +because we wanted to align call closer to `Function.prototype.call`. You can still use +`yield* call(() => Promise.resolve(42))` and `yield* call(async function() {})`. In v4, we plan to ship +a new function called `when` which will take a promise and wrap it in a constructor for convenience. diff --git a/www/docs/structure.json b/www/docs/structure.json index 09b1640a..9e748998 100644 --- a/www/docs/structure.json +++ b/www/docs/structure.json @@ -17,6 +17,7 @@ ["context.mdx", "Context"] ], "Advanced": [ + ["faq.mdx", "FAQ"], ["scope.mdx", "Scope"], ["processes.mdx", "Processes"] ]