graphile-build
Version:
Build a GraphQL schema from plugins
103 lines (95 loc) • 2.64 kB
Flow
// @flow
/* eslint-disable flowtype/no-weak-types */
// Turn a callback-based listener into an async iterator
// From https://raw.githubusercontent.com/withspectrum/callback-to-async-iterator/master/src/index.js
// License MIT (Copyright (c) 2017 Maximilian Stoiber)
// Based on https://github.com/apollographql/graphql-subscriptions/blob/master/src/event-emitter-to-async-iterator.ts
import { $$asyncIterator } from "iterall";
const defaultOnError = (err: Error) => {
throw err;
};
export default function callbackToAsyncIterator<
CallbackInput: any,
ReturnVal: any
>(
listener: ((arg: CallbackInput) => any) => ?ReturnVal | Promise<?ReturnVal>,
options?: {
onError?: (err: Error) => void,
onClose?: (arg?: ?ReturnVal) => void,
buffering?: boolean,
} = {}
) {
const { onError = defaultOnError, buffering = true, onClose } = options;
let pullQueue = [];
let pushQueue = [];
let listening = true;
let listenerReturnValue;
function pushValue(value) {
if (pullQueue.length !== 0) {
pullQueue.shift()({ value, done: false });
} else if (buffering === true) {
pushQueue.push(value);
}
}
function pullValue() {
return new Promise(resolve => {
if (pushQueue.length !== 0) {
resolve({ value: pushQueue.shift(), done: false });
} else {
pullQueue.push(resolve);
}
});
}
function emptyQueue() {
if (listening) {
listening = false;
pullQueue.forEach(resolve => resolve({ value: undefined, done: true }));
pullQueue = [];
pushQueue = [];
onClose && onClose(listenerReturnValue);
}
}
try {
// Start listener
Promise.resolve(listener(value => pushValue(value)))
.then(a => {
listenerReturnValue = a;
})
.catch(err => {
onError(err);
});
return {
next(): Promise<{ value?: CallbackInput, done: boolean }> {
return listening ? pullValue() : this.return();
},
return(): Promise<{ value: typeof undefined, done: boolean }> {
emptyQueue();
return Promise.resolve({ value: undefined, done: true });
},
throw(error: Error) {
emptyQueue();
onError(error);
return Promise.reject(error);
},
[$$asyncIterator]() {
return this;
},
};
} catch (err) {
onError(err);
return {
next() {
return Promise.reject(err);
},
return() {
return Promise.reject(err);
},
throw(error: Error) {
return Promise.reject(error);
},
[$$asyncIterator]() {
return this;
},
};
}
}