Skip to content

Instantly share code, notes, and snippets.

@ryunp
Created February 29, 2020 12:09
Show Gist options
  • Save ryunp/a48aec0d636fd847c162b41d4e40748b to your computer and use it in GitHub Desktop.
Save ryunp/a48aec0d636fd847c162b41d4e40748b to your computer and use it in GitHub Desktop.
Testing async generator and iterators
/**
* Date: 2/29/20
* Author: Ryan Paul
* Description:
* Oversimplified API mockup with paginated query behavior,
* testing async generator functions with async iterators.
*/
// Stateless helpers
var randBetween = (min, max) => min + Math.floor(Math.random() * (max - min))
var asyncSleep = delay => new Promise(res => setTimeout(() => res(true), delay))
var generateItem = int => ({ id: int, title: `Data ${int}!`, data: randBetween(1, 99999) })
var generateItemSet = count => [...Array(count).keys()].map(generateItem)
// Mockup 'paginated' async storage facility with query resitrictions
// Avoids class/method by simply returning data accessor to closured state
function generateAsyncStorage ({ dataSizeRange = [150, 400], queryLimit = 100 } = {}) {
const dataSet = generateItemSet(randBetween(...dataSizeRange))
console.log(`Generated ${dataSet.length} storage items`)
// Async data accessor returned
return function queryData ({ prevIndex = 0, first = 20 } = {}) {
return new Promise(executor)
function executor (resolve, reject) {
if (first > queryLimit) first = queryLimit
var nextIdx = prevIndex + first
const endIdx = nextIdx < dataSet.length ? nextIdx : dataSet.length - 1
const data = dataSet.slice(prevIndex, endIdx)
const lastIndex = endIdx < dataSet.length - 1 ? endIdx : null
// Arbitrary 50-200ms response time
setTimeout(() => resolve({ data, lastIndex }), randBetween(50, 200))
}
}
}
// Create storage, save accessor function
var queryData = generateAsyncStorage()
// Test: Simple unmanaged query
function singleQuery () {
console.log('Single Query')
return queryData({ prevIndex: 24, first: 69 }).then(console.log)
}
// Test: Sequential stateful querying
async function iterationQuery () {
console.log('Iterator Query')
// Storage facility for the paged response data
const dataCollection = new Map()
// ES6 magic
for await (streams of queryDataAllThrottled(queryData, 2000)) {
for (stream of streams) { dataCollection.set(stream.id, stream) }
console.log(`${streams.length} added`)
}
// Show final aggregation
console.log(Array.from(dataCollection.values()).map(d => d.title))
// Throttled async iterator for paged data aggregation
// Note: Throttling is grossly oversimplified, need to test
// a proper stateful implementation (queue structure of sorts)
async function* queryDataAllThrottled (promise, delay) {
var prevIndex = 0
do {
var response = await promise({ prevIndex, first: 100 })
prevIndex = response.lastIndex
yield response.data
} while (response.lastIndex && await asyncSleep(delay))
}
}
// Run tests
void Promise.resolve().then(singleQuery).then(iterationQuery)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment