might be helpful to know!
There’s an interesting parallel between Observable and the Unity game engine—they both use generators in almost identical ways.
Unity was released in 2005 with an async runtime it called Coroutines on top of C# generators. It’s still heavily used today to write asynchronous scripts. For example, this fades out a color:
IEnumerator Fade() {
for (float f = 1f; f >= 0; f -= 0.1f) {
Color c = renderer.material.color;
c.a = f;
renderer.material.color = c;
yield return null; // <-- continue at next frame
// yield return new WaitForSeconds(.1f); // <-- or continue in 0.1s
}
}
StartCoroutine("foo"); // <-- Schedules the generator to start
StopCoroutine("foo"); // <-- Cancels the generator it at next yield
In Observable:
{
for (const f = 1; f >= 0; f -= 0.1) {
const c = renderer.material.color;
c.a = f;
renderer.material.color = c;
yield null; // <-- continue at next frame
// yield Promises.delay(0.1); // <-- continue in 0.1s
}
}
// Schedules the generator to start when cell is evaluated.
// Cancels the generator at next yield (using `.return()`) when cell is re-evaluated.
It’s also interesting that this asynchronous yielding pattern has crystallized recently into a popular language-level feature for Python, C#, Hack, and JavaScript by allowing both yield
s and await
s in a function—rather than relying on a custom scheduler to check the yielded values for async events. Glad to see Observable take advantage of this pretty quickly after release, whereas Unity is still waiting on the C# proposal to finalize.
Has this pattern been used anywhere else? I’d be interested in tracing more of its history.