Created
February 29, 2020 12:09
-
-
Save ryunp/a48aec0d636fd847c162b41d4e40748b to your computer and use it in GitHub Desktop.
Testing async generator and iterators
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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