Skip to content

Instantly share code, notes, and snippets.

@darkleaf
Last active September 4, 2021 17:11
Show Gist options
  • Save darkleaf/da603a1da4ead8d982f9fc9c0bc243e2 to your computer and use it in GitHub Desktop.
Save darkleaf/da603a1da4ead8d982f9fc9c0bc243e2 to your computer and use it in GitHub Desktop.

select implementation:

//const all = naiveAll
const all = batchedAll
function* getFullPost(id) {
const post = yield ['getPost', id]
const [author, comments] = yield* all([
getFullUser(post.authorId),
all(post.commentIds.map(id => getFullComment(id))),
])
return {
...post,
author,
comments,
}
}
function* getFullUser(id) {
const user = yield ['getUser', id]
return user
}
function* getFullComment(id) {
const comment = yield ['getComment', id]
const author = yield* getFullUser(comment.authorId)
return {
...comment,
author,
}
}
function* getData(postIds) {
return yield* all(postIds.map(id => getFullPost(id)))
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function* naiveAll(gens) {
const res = []
for (const g of gens) {
res.push(yield* g)
}
return res
}
function* batchedAll(gens) {
let lengths = [], batches = []
let colengths = [], cobatches = []
let values = [], covalues = []
const results = new Map()
do {
for (const g of gens) {
if (results.has(g)) continue
const colength = colengths.shift()
const cobatch = cobatches.shift()
let covalue
if (cobatch) {
covalue = covalues.splice(0, colength)
} else {
covalue = covalues.shift()
}
const {done, value} = g.next(covalue)
if (done) {
results.set(g, value)
continue
}
const [tag, arg] = value
if (tag === 'batch') {
lengths.push(arg.length)
batches.push(true)
values = values.concat(arg)
} else {
lengths.push(1)
batches.push(false)
values.push(value)
}
}
if (results.size == gens.length) break
covalues = yield ['batch', values]
colengths = lengths
cobatches = batches
values = []
lengths = []
batches = []
} while(true)
return gens.map(g => results.get(g))
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
const db = {
users: {
1: {login: 'john'},
2: {login: 'jane'},
},
posts: {
1: {
title: 'a',
authorId: '1',
commentIds: ['1', '2'],
},
2: {
title: 'b',
authorId: '2',
commentIds: ['3', '4'],
},
},
comments: {
1: {
text: 'q',
authorId: '1',
},
2: {
text: 'w',
authorId: '2',
},
3: {
text: 'e',
authorId: '1',
},
4: {
text: 'r',
authorId: '2',
},
}
}
function wait(data, delay) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(data), delay)
})
}
function getResource(type, id) {
//console.log("getResource: ", type, id)
return wait(
db[type][id],
1000,
)
}
function getResources(type, ids) {
//console.log("getResources: ", type, ids)
return wait(
ids.map(id => db[type][id]),
1000,
)
}
const batchHandlers = {
getUser: ids => getResources('users', ids),
getPost: ids => getResources('posts', ids),
getComment: ids => getResources('comments', ids),
}
const handlers = {
getUser: id => getResource('users', id),
getPost: id => getResource('posts', id),
getComment: id => getResource('comments', id),
batch: async values => {
const argsByTag = {}
const indexesByTag = {}
for (const [idx, value] of values.entries()) {
const [tag, arg] = value
argsByTag[tag] ||= []
argsByTag[tag].push(arg)
indexesByTag[tag] ||= []
indexesByTag[tag].push(idx)
}
const tags = Object.keys(argsByTag)
const resp = await Promise.all(
tags.map(tag => {
const args = argsByTag[tag]
return batchHandlers[tag](args)
})
)
const res = []
res.length = values.length
for (const [respIdx, tag] of tags.entries()) {
const indexes = indexesByTag[tag]
for (let i = 0; i < indexes.length; i++) {
res[indexes[i]] = resp[respIdx][i]
}
}
return res
}
}
async function handle(gen) {
let done, value, covalue
do {
;({value, done} = gen.next(covalue))
if (done) return value
const [tag, arg] = value
covalue = await handlers[tag](arg)
console.log('handle:', tag, arg, covalue)
} while(true)
}
//const all = naiveAll
const all = batchedAll
handle(getData(['1', '2']))
.then(r => JSON.stringify(r, null, 2))
.then(r => console.log(r))
$ node effect.js
handle: getPost 1 { title: 'a', authorId: '1', commentIds: [ '1', '2' ] }
handle: getUser 1 { login: 'john' }
handle: getComment 1 { text: 'q', authorId: '1' }
handle: getUser 1 { login: 'john' }
handle: getComment 2 { text: 'w', authorId: '2' }
handle: getUser 2 { login: 'jane' }
handle: getPost 2 { title: 'b', authorId: '2', commentIds: [ '3', '4' ] }
handle: getUser 2 { login: 'jane' }
handle: getComment 3 { text: 'e', authorId: '1' }
handle: getUser 1 { login: 'john' }
handle: getComment 4 { text: 'r', authorId: '2' }
handle: getUser 2 { login: 'jane' }
[
{
"title": "a",
"authorId": "1",
"commentIds": [
"1",
"2"
],
"author": {
"login": "john"
},
"comments": [
{
"text": "q",
"authorId": "1",
"author": {
"login": "john"
}
},
{
"text": "w",
"authorId": "2",
"author": {
"login": "jane"
}
}
]
},
{
"title": "b",
"authorId": "2",
"commentIds": [
"3",
"4"
],
"author": {
"login": "jane"
},
"comments": [
{
"text": "e",
"authorId": "1",
"author": {
"login": "john"
}
},
{
"text": "r",
"authorId": "2",
"author": {
"login": "jane"
}
}
]
}
]
$ node effect.js
handle: batch [ [ 'getPost', '1' ], [ 'getPost', '2' ] ] [
{ title: 'a', authorId: '1', commentIds: [ '1', '2' ] },
{ title: 'b', authorId: '2', commentIds: [ '3', '4' ] }
]
handle: batch [
[ 'getUser', '1' ],
[ 'getComment', '1' ],
[ 'getComment', '2' ],
[ 'getUser', '2' ],
[ 'getComment', '3' ],
[ 'getComment', '4' ]
] [
{ login: 'john' },
{ text: 'q', authorId: '1' },
{ text: 'w', authorId: '2' },
{ login: 'jane' },
{ text: 'e', authorId: '1' },
{ text: 'r', authorId: '2' }
]
handle: batch [
[ 'getUser', '1' ],
[ 'getUser', '2' ],
[ 'getUser', '1' ],
[ 'getUser', '2' ]
] [
{ login: 'john' },
{ login: 'jane' },
{ login: 'john' },
{ login: 'jane' }
]
[
{
"title": "a",
"authorId": "1",
"commentIds": [
"1",
"2"
],
"author": {
"login": "john"
},
"comments": [
{
"text": "q",
"authorId": "1",
"author": {
"login": "john"
}
},
{
"text": "w",
"authorId": "2",
"author": {
"login": "jane"
}
}
]
},
{
"title": "b",
"authorId": "2",
"commentIds": [
"3",
"4"
],
"author": {
"login": "jane"
},
"comments": [
{
"text": "e",
"authorId": "1",
"author": {
"login": "john"
}
},
{
"text": "r",
"authorId": "2",
"author": {
"login": "jane"
}
}
]
}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment