nice-grpc-web
Version:
A Browser gRPC library that is nice to you
87 lines (72 loc) • 2.07 kB
text/typescript
// Credit: IxJS authors
// https://github.com/ReactiveX/IxJS/blob/v4.5.1/src/asynciterable/asyncsink.ts
// This implementation has a slight change: it silently ignores items pushed
// after end instead of throwing an error.
const ARRAY_VALUE = 'value';
const ARRAY_ERROR = 'error';
interface AsyncSinkItem<T> {
type: string;
value?: T;
error?: any;
}
interface AsyncResolver<T> {
resolve: (value: T | PromiseLike<T>) => void;
reject: (reason: any) => void;
}
/** @internal */
export class AsyncSink<TSource> implements AsyncIterableIterator<TSource> {
private _ended: boolean;
private _values: AsyncSinkItem<TSource>[];
private _resolvers: AsyncResolver<IteratorResult<TSource>>[];
constructor() {
this._ended = false;
this._values = [];
this._resolvers = [];
}
[Symbol.asyncIterator]() {
return this;
}
write(value: TSource) {
this._push({type: ARRAY_VALUE, value});
}
error(error: any) {
this._push({type: ARRAY_ERROR, error});
}
private _push(item: AsyncSinkItem<TSource>) {
if (this._ended) {
return;
}
if (this._resolvers.length > 0) {
const {resolve, reject} = this._resolvers.shift()!;
if (item.type === ARRAY_ERROR) {
reject(item.error!);
} else {
resolve({done: false, value: item.value!});
}
} else {
this._values.push(item);
}
}
next() {
if (this._values.length > 0) {
const {type, value, error} = this._values.shift()!;
if (type === ARRAY_ERROR) {
return Promise.reject(error);
} else {
return Promise.resolve({done: false, value} as IteratorResult<TSource>);
}
}
if (this._ended) {
return Promise.resolve({done: true} as IteratorResult<TSource>);
}
return new Promise<IteratorResult<TSource>>((resolve, reject) => {
this._resolvers.push({resolve, reject});
});
}
end() {
while (this._resolvers.length > 0) {
this._resolvers.shift()!.resolve({done: true} as IteratorResult<TSource>);
}
this._ended = true;
}
}