@graphql-tools/utils
Version:
Common package containing utils and types for GraphQL tools
83 lines (82 loc) • 2.47 kB
JavaScript
import { fakePromise } from './fakePromise.js';
export function observableToAsyncIterable(observable) {
const pullQueue = [];
const pushQueue = [];
let listening = true;
const pushValue = (value) => {
if (pullQueue.length !== 0) {
// It is safe to use the ! operator here as we check the length.
pullQueue.shift()({ value, done: false });
}
else {
pushQueue.push({ value, done: false });
}
};
const pushError = (error) => {
if (pullQueue.length !== 0) {
// It is safe to use the ! operator here as we check the length.
pullQueue.shift()({ value: { errors: [error] }, done: false });
}
else {
pushQueue.push({ value: { errors: [error] }, done: false });
}
};
const pushDone = () => {
if (pullQueue.length !== 0) {
// It is safe to use the ! operator here as we check the length.
pullQueue.shift()({ done: true });
}
else {
pushQueue.push({ done: true });
}
};
const pullValue = () => new Promise(resolve => {
if (pushQueue.length !== 0) {
const element = pushQueue.shift();
// either {value: {errors: [...]}} or {value: ...}
resolve(element);
}
else {
pullQueue.push(resolve);
}
});
const subscription = observable.subscribe({
next(value) {
return pushValue(value);
},
error(err) {
return pushError(err);
},
complete() {
return pushDone();
},
});
const emptyQueue = () => {
if (listening) {
listening = false;
subscription.unsubscribe();
for (const resolve of pullQueue) {
resolve({ value: undefined, done: true });
}
pullQueue.length = 0;
pushQueue.length = 0;
}
};
return {
next() {
// return is a defined method, so it is safe to call it.
return listening ? pullValue() : this.return();
},
return() {
emptyQueue();
return fakePromise({ value: undefined, done: true });
},
throw(error) {
emptyQueue();
return Promise.reject(error);
},
[Symbol.asyncIterator]() {
return this;
},
};
}