response-iterator
Version:
Creates an async iterator for a variety of inputs in the browser and node. Supports fetch, node-fetch, and cross-fetch
77 lines (76 loc) • 2.08 kB
JavaScript
const hasIterator = typeof Symbol !== 'undefined' && Symbol.asyncIterator;
/* c8 ignore start */ export default function nodeStreamIterator(stream) {
let cleanup = null;
let error = null;
let done = false;
const data = [];
const waiting = [];
function onData(chunk) {
if (error) return;
if (waiting.length) return waiting.shift()[0]({
value: chunk,
done: false
});
data.push(chunk);
}
function onError(err) {
error = err;
const all = waiting.slice();
all.forEach((pair)=>{
pair[1](err);
});
!cleanup || cleanup();
}
function onEnd() {
done = true;
const all = waiting.slice();
all.forEach((pair)=>{
pair[0]({
value: undefined,
done: true
});
});
!cleanup || cleanup();
}
cleanup = ()=>{
cleanup = null;
stream.removeListener('data', onData);
stream.removeListener('error', onError);
stream.removeListener('end', onEnd);
stream.removeListener('finish', onEnd);
stream.removeListener('close', onEnd);
};
stream.on('data', onData);
stream.on('error', onError);
stream.on('end', onEnd);
stream.on('finish', onEnd);
stream.on('close', onEnd);
function getNext() {
return new Promise((resolve, reject)=>{
if (error) return reject(error);
if (data.length) return resolve({
value: data.shift(),
done: false
});
if (done) return resolve({
value: undefined,
done: true
});
waiting.push([
resolve,
reject
]);
});
}
const iterator = {
next () {
return getNext();
}
};
if (hasIterator) {
iterator[Symbol.asyncIterator] = function() {
return this;
};
}
return iterator;
} /* c8 ignore stop */