Skip to content

Instantly share code, notes, and snippets.

@zenparsing
Created April 8, 2016 20:57
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zenparsing/2faccd1ce5e786f65ea1555d8d677cf7 to your computer and use it in GitHub Desktop.
Save zenparsing/2faccd1ce5e786f65ea1555d8d677cf7 to your computer and use it in GitHub Desktop.
Combinators with Async Generators
function once(el, event) {
return new Promise((resolve, reject) => {
const handler = e => {
resolve(e);
el.removeEventListener(event, handler);
};
el.addEventListener(event, handler);
});
};
async function* fromEvent(el, event) {
while (true) yield await once(el, event);
}
async function* map(fn) {
for await (const value of this) yield fn(value);
};
async function* filter(fn) {
for await (const value of this) if (fn(value)) yield value;
}
async function* distinctUntilChanged() {
let prev = {};
for await (const value of this)
if (value !== prev)
yield prev = value;
}
function delay(ms, value) {
return new Promise(resolve => {
setTimeout(() => resolve(value), ms);
});
}
async function* peek() {
const iter = this[Symbol.asyncIterator]();
let result;
try {
let next = iter.next();
while (true) {
result = await next;
if (result.done) return result.value;
yield [result.value, next = iter.next()];
}
} finally {
if (!(result && result.done))
iter.return();
}
}
async function* flatMapLatest(fn) {
const cancel = {};
for await (const [value, next] of this::peek()) {
const result = await Promise.race([
fn(value),
next.then(() => cancel),
]);
if (result !== cancel)
yield result;
}
}
function debounce(amount) {
return this::flatMapLatest(value => {
return delay(amount).then(() => value);
});
}
function fakeFetch(url) {
return delay(50).then(() => {
return {
json() { return Promise.resolve({ fetched_url: url }) }
}
});
}
function stream(el) {
return fromEvent(el, "keyup")
::map(e => e.target.value)
::filter(Boolean)
::debounce(300)
::distinctUntilChanged()
::flatMapLatest(v => fakeFetch(`/autocomplete/${v}`).then(x => x.json()));
}
/* Inserts an input element into the esdown REPL and starts the stream */
(async () => {
const d = document.createElement("div");
d.innerHTML = `<label>Type Here:
<input id="demo-input" style="font-size: 16px; padding: 4px; width: 300px" />
</label>`;
contentElement.insertBefore(d, contentElement.firstChild);
const input = d.querySelector("#demo-input");
for await (const value of stream(input))
console.log(value);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment