UNPKG

suspenders-js

Version:

Asynchronous programming library utilizing coroutines, functional reactive programming and structured concurrency.

135 lines 4.58 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const Channel_1 = require("./Channel"); const Flow_1 = require("./Flow"); const Scope_1 = require("./Scope"); const Util_1 = require("./Util"); // Structured concurrency // Scopes have ownership of coroutines. Coroutines are launched in a scope. If that scope is // canceled, then any coroutines and subscopes within it will be canceled as well. If a coroutine // throws an error, it cancels it's owning scope with an error. Scopes canceled with an error // will bubble the the error up to it's parent, canceling the whole tree. // For example if scope1 is canceled all coroutines would be canceled in the graph below. But if // scope2 is canceled, only coroutine3 and coroutine4 are canceled. If coroutine3 throws an error, // all the coroutines and scopes are canceled. // scope1 // | // +- coroutine1 // | // +- coroutine2 // | // +- scope2 // | // + coroutine3 // | // + coroutine4 const scope = new Scope_1.Scope(); // Flows emit multiple async values. They provide a typical functional interface like map() and // .filter(). Flows are cold, meaning they don't emit values unless they have an observer. Flow_1.flowOf((collector) => function* () { for (let i = 1; i <= 200; i++) { collector.emit(i); } }) .filter(x => x % 2 === 1) .map(x => x + x) .onEach(x => console.log(x)) // This will start the flow in scope. .launchIn(scope); // This starts a coroutine that consumes two flows in order. scope.launch(function* () { // suspends until flow completes yield Flow_1.flowOf((collector) => function* () { for (let i = 1; i <= 200; i++) { collector.emit(i); } }) .filter(x => x % 2 === 1) .map(x => x + x) // Collect() consumes the flow until it completes. // Resumes the coroutine once the flow has completed. .collect(x => console.log(x)); yield Flow_1.flowOf((collector) => function* () { for (let i = 1; i <= 200; i++) { collector.emit(i); } }) .filter(x => x % 2 === 1) .map(x => x + x) .collect(x => console.log(x)); }); // Channels are for communication between coroutines. const channel = new Channel_1.Channel(); // Producer/consumer coroutines communicating through a channel. scope.launch(function* () { for (let i = 1; i <= 200; i++) { yield channel.send(i); } }); scope.launch(function* () { for (;;) { const x = yield* this.suspend(channel.receive); if (x % 2 === 1) { const y = x + x; console.log(y); } } }); // Transform() is a powerful way to process values emitted by a flow. It takes a coroutine that can // perform more asynchronous tasks and emit 0 or more values downstream. const eventSubject = new Flow_1.EventSubject(); scope.launch(function* () { yield eventSubject .transform((x, collector) => function* () { if (x % 2 === 1) { yield Util_1.wait(10); collector.emit(x + x); } }) .collect(x => { console.log(x); }); }); // Pushes events to observers on eventSubject. for (let i = 1; i <= 200; i++) { eventSubject.emit(i); } // Calling another coroutine from a coroutine. function* anotherCoroutine() { yield Util_1.wait(100); // This doesn't wait for the result of the launched coroutine. this.launch(function* () { yield Util_1.wait(200); }); return 1; } scope.launch(function* () { // This ensures all coroutines launched from anotherCoroutine() are completed before resuming. const x = yield* this.call(anotherCoroutine); console.log(x); }); scope.launch(function* () { // This will resume before all coroutines launched from anotherCoroutine() have completed. const x = yield* anotherCoroutine.call(this); console.log(x); }); // Asynchronously call coroutines. function* jobA() { yield Util_1.wait(100); return 1; } function* jobB() { yield Util_1.wait(200); return 2; } scope.launch(function* () { // Runs both jobs concurrently. const [resultA, resultB] = yield* this.suspend2(this.callAsync(jobA), this.callAsync(jobB)); console.log(`${resultA} ${resultB}`); }); scope.launch(function* () { // Races jobA with jobB to get the faster result. Cancels the slower job. const fastestResult = yield* this.suspend(Util_1.race(this.callAsync(jobA), this.callAsync(jobB))); console.log(fastestResult); }); //# sourceMappingURL=Example.js.map