UNPKG

monoflux

Version:

Stream processing for JS simplified

336 lines 9.86 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Flux = void 0; class Flux { generator; upstream; handleCancel; _closed = false; constructor(generator, upstream, handleCancel) { const thiss = this; this.generator = (async function* gen() { try { yield* generator; } catch (e) { yield ignorableValue; throw e; } finally { thiss._closed = true; } })(); this.upstream = upstream; this.handleCancel = handleCancel; } //// Start AsyncGenerator methods //// [Symbol.asyncIterator]() { return this; } async next(...args) { const value = await this.generator.next(args); return value.value !== ignorableValue ? value : await this.next(args); } return(value) { return this.generator.return(value); } throw(e) { return this.generator.throw(e); } async cancel(e) { if (this.closed) { return; } if (this.upstream !== undefined) { return this.upstream.cancel(e); } else { if (this.handleCancel !== undefined) { this.handleCancel(e); } else { await this.throw(e); } } } //// End AsyncGenerator methods //// //// Start Promise methods //// async then(onfulfilled, onrejected) { try { const result = await this.asList(); return onfulfilled !== undefined && onfulfilled !== null ? await onfulfilled(result) : result; } catch (e) { if (onrejected !== undefined && onrejected !== null) { return onrejected(e); } else { throw e; } } } async catch(onrejected) { try { return this.asList(); } catch (e) { if (onrejected !== undefined && onrejected !== null) { return onrejected(e); } else { throw e; } } } async finally(onfinally) { const promise = await this.asList(); onfinally?.(); return promise; } get [Symbol.toStringTag]() { return 'Flux'; } //// End Promise methods //// subscribe(callback) { const flux = callback !== undefined ? this.doOnEach(callback) : this; const recur = () => { flux .next() .then(value => { if (!value.done) { recur(); } }) .catch(err => { console.error(err); }); }; recur(); return { unsubscribe: () => this.return() }; } filter(predicate) { const thiss = this; return Flux.constructFromGeneratorFunction(async function* gen() { for await (const value of thiss) { if (predicate(value)) { yield value; } } }, this); } /** * Flux until predicate is true; the rest is dropped. The first * value that is dropped is the value for which the predicate * is true. * * @param predicate */ untilExcl(predicate) { const thiss = this; return Flux.constructFromGeneratorFunction(async function* gen() { for await (const value of thiss) { if (predicate(value)) { break; } yield value; } }, this); } doOnEach(callback) { const thiss = this; return Flux.constructFromGeneratorFunction(async function* gen() { for await (const value of thiss) { callback(value); yield value; } }, this); } doAfterLast(callback) { const thiss = this; const events = []; return Flux.constructFromGeneratorFunction(async function* gen() { for await (const value of thiss) { events.push(value); yield value; } await callback(events); }, this); } map(mapper) { const thiss = this; return Flux.constructFromGeneratorFunction(async function* gen() { for await (const value of thiss) { yield mapper(value); } }, this); } take(n) { const thiss = this; return Flux.constructFromGeneratorFunction(async function* gen() { let i = 0; for await (const value of thiss) { if (i >= n) { thiss.cancel(); break; } i++; yield value; } }, this); } flatMap(mapper) { const thiss = this; return Flux.constructFromGeneratorFunction(async function* gen() { for await (const value of thiss) { const result = mapper(value); if (Array.isArray(result)) { for (const r of result) { yield r; } } else { yield await result; } } }, this); } transform(defineGenerator) { const definedGenerator = defineGenerator(this); return Flux.constructFromGeneratorFunction(async function* gen() { for await (const value of definedGenerator) { yield value; } }, this); } async reduce(reducer, initialValue) { let reduction = initialValue; for await (const value of this) { reduction = reducer(reduction, value); } return reduction; } async asList() { const result = []; for await (const value of this) { result.push(value); } return result; } async whenComplete() { for await (const ignored of this) { // do nothing } } static create(creator) { const values = []; const resolvers = []; let isDone = false; let error = undefined; const push = (value) => { if (isDone) { throw new Error("Cannot push to a completed generator"); } if (resolvers.length > 0) { const resolve = resolvers.shift(); resolve.resolve({ done: false, value }); } else { values.push(value); } }; const complete = () => { isDone = true; while (resolvers.length > 0) { const resolve = resolvers.shift(); resolve.resolve({ done: true, value: undefined }); } }; const reject = (err) => { error = { value: err }; // Wake up next resolver to propagate the error resolvers.shift()?.reject(err); // This will make the `.next()` promise reject with the error }; (async () => { try { await creator(push, complete, reject); } catch (err) { reject(err); } })(); return Flux.fromGenerator({ [Symbol.asyncIterator]() { return this; }, next(...args) { if (error) { const err = error.value; error = undefined; return Promise.reject(err); } if (values.length > 0) { const value = values.shift(); return Promise.resolve({ done: false, value }); } if (isDone) { return Promise.resolve({ done: true, value: undefined }); } return new Promise((resolve, reject) => { resolvers.push({ resolve, reject }); }); }, return() { complete(); return Promise.resolve({ done: true, value: undefined }); }, throw(err) { reject(err); return Promise.reject(err); }, }); } static fromArray(array) { return Flux.fromGeneratorFunction(async function* gen() { for (const value of array) { yield value; } }); } static constructFromGeneratorFunction(fn, upstream, handleCancel) { return new Flux(fn(), upstream, handleCancel); } static fromGeneratorFunction(fn, handleCancel) { return Flux.constructFromGeneratorFunction(fn, undefined, handleCancel); } static fromGenerator(generator, handleCancel) { return new Flux(generator, undefined, handleCancel); } static fromReadableStream(stream, handleCancel) { return Flux.fromGeneratorFunction(async function* gen() { const reader = stream.getReader(); try { let excerpt = undefined; while (!(excerpt = await reader.read()).done) { yield excerpt.value; } } finally { await reader.cancel(); } }, handleCancel); } get closed() { return this._closed; } } exports.Flux = Flux; const ignorableValue = {}; //# sourceMappingURL=Flux.js.map