@lodestar/beacon-node
Version:
A Typescript implementation of the beacon chain
120 lines • 4.32 kB
JavaScript
import { LinkedList } from "./array.js";
import { fromThreadBoundaryError, toThreadBoundaryError } from "./error.js";
export var IteratorEventType;
(function (IteratorEventType) {
IteratorEventType["next"] = "iterator.next";
IteratorEventType["done"] = "iterator.done";
IteratorEventType["error"] = "iterator.error";
})(IteratorEventType || (IteratorEventType = {}));
export class AsyncIterableBridgeCaller {
constructor(events) {
this.events = events;
this.nextRequestId = 0;
// TODO: Consider expiring the requests after no reply for long enough, t
this.pending = new Map();
events.onResponse(this.onResponse.bind(this));
}
get pendingCount() {
return this.pending.size;
}
getAsyncIterable(callArgs) {
const self = this;
return {
[Symbol.asyncIterator]() {
const id = self.nextRequestId++;
const req = {
items: new LinkedList(),
done: false,
error: null,
onNext: null,
};
self.pending.set(id, req);
self.events.emitRequest({
callArgs,
id,
});
return {
async next() {
while (true) {
const item = req.items.shift();
if (item !== null) {
return { value: item, done: false };
}
if (req.error) {
throw req.error;
}
if (req.done) {
// Is it correct to return undefined on done: true?
return { value: undefined, done: true };
}
await new Promise((resolve) => {
req.onNext = resolve;
});
}
},
async return() {
// This will be reached if the consumer called 'break' or 'return' early in the loop.
self.pending.delete(id);
return { value: undefined, done: true };
},
};
},
};
}
onResponse(data) {
const req = this.pending.get(data.id);
if (!req) {
// TODO: Log or track, can happen if consumer returns source early
return;
}
switch (data.type) {
case IteratorEventType.done:
// What if it's already done?
req.done = true;
// Do not expect more responses
this.pending.delete(data.id);
break;
case IteratorEventType.error:
// What if there's already an error?
req.error = fromThreadBoundaryError(data.error);
// Do not expect more responses
this.pending.delete(data.id);
break;
case IteratorEventType.next:
// Should check that's it's already done or error?
req.items.push(data.item);
break;
}
req.onNext?.();
}
}
export class AsyncIterableBridgeHandler {
constructor(events, handler) {
this.events = events;
this.handler = handler;
events.onRequest(this.onRequest.bind(this));
}
async onRequest(data) {
try {
for await (const item of this.handler(data.callArgs)) {
this.events.emitResponse({
type: IteratorEventType.next,
id: data.id,
item,
});
}
this.events.emitResponse({
type: IteratorEventType.done,
id: data.id,
});
}
catch (e) {
this.events.emitResponse({
type: IteratorEventType.error,
id: data.id,
error: toThreadBoundaryError(e),
});
}
}
}
//# sourceMappingURL=asyncIterableToEvents.js.map