monoflux
Version:
Stream processing for JS simplified
336 lines • 9.86 kB
JavaScript
;
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