Skip to content

Instantly share code, notes, and snippets.

@zerkalica
Last active October 13, 2023 08:57
Show Gist options
  • Save zerkalica/dd2a7aa7a5fed51f9aabca5a415987c6 to your computer and use it in GitHub Desktop.
Save zerkalica/dd2a7aa7a5fed51f9aabca5a415987c6 to your computer and use it in GitHub Desktop.
batch
namespace $ {
type Unit<Full, Patch> = ReturnType<typeof $mol_promise<Full | null>> & {
id: string
patch?: Patch | null
}
export class $gd_kit_batch<
Full extends {},
Patch extends ({} | null) = Partial<Full>,
> extends $mol_object {
protected sync_timeout = undefined as undefined | $mol_after_timeout
id() {
return ''
}
destructor() {
this.units = []
this.sync_timeout?.destructor()
this.sync_timeout = undefined
}
@ $mol_action
load(ids: readonly string[]): Record<string, Full> {
throw new Error('implement')
}
@ $mol_action
patch(patches: Record<string, Patch | null>): Record<string, Full> {
throw new Error('implement')
}
strict_ids_check() {
return false
}
protected units = [] as Unit<Full, Patch>[]
protected async batch(stackSaveError: Error) {
this.sync_timeout = undefined
const units = this.units
if( units.length === 0 ) return
this.units = []
const load_ids = new Set<string>()
const patches = {} as Record<string, Patch | null>
let has_patches = false
for (const { id, patch } of units) {
if (patch === undefined) {
load_ids.add(id)
continue
}
patches[id] = patch === null ? null : Object.assign(patches[id] ?? {}, patch)
has_patches = true
}
let not_fetched = units
try {
const t = $mol_wire_async(this)
const [ loaded, patched ] = await Promise.all([
load_ids.size ? t.load([ ...load_ids ]) : undefined,
has_patches ? t.patch(patches) : undefined,
])
const strict_check = this.strict_ids_check()
not_fetched = []
for( const unit of units ) {
const val = patched?.[unit.id] ?? loaded?.[unit.id] ?? null
if (! val && strict_check) not_fetched.push(unit)
else unit.done(val)
}
if (not_fetched.length) {
throw new Error(`Not fetched`)
}
} catch (e) {
const err = e instanceof Error ? e : new Error(String(e))
try {
Object.defineProperty(err, 'message', {
value: `${err.message}, ids: "${not_fetched.map(({ id }) => id).join('", "')}"`
})
} catch (e) {}
err.stack = (err.stack ?? '') + (stackSaveError.stack?.split('\n').slice(1).join('\n') ?? '')
for( const unit of not_fetched ) unit.fail(err)
}
}
timeout() {
return 70
}
data_async(id: string, patch?: Patch | null) {
const prev = this.units.find(u => u.id === id)
if (prev && patch) prev.patch = { ...prev.patch, ...patch }
if (prev) return prev
const unit: Unit<Full, Patch> = Object.assign($mol_promise<Full | null>(), { id, patch })
this.units.push(unit)
if (this.sync_timeout) return unit
const error = new Error('Stack save') // new Error() сохраняет стек трейс для отладки
this.sync_timeout = new this.$.$mol_after_timeout(
this.timeout(),
this.batch.bind(this, error)
)
return unit
}
cache(id: string, cache?: Full | Error | null) {
return this.data(id, null, cache)
}
static level = 0
static promises = {} as Record<string, Unit<any, any>>
static wait_async() {
return Promise.all(Object.values(this.promises))
}
@ $mol_action
static wait(id: string) {
return this.promises[id] ?? null
}
static transaction<V>(task: () => V): V {
try {
const prev_level = this.level
this.level++
const result = task()
// if (prev_level === 0) $mol_wire_sync(this).wait_async()
this.promises = {}
return result
} finally {
this.level--
}
}
@ $mol_mem_key
data(id: string, patch?: Patch | null, cache?: Full | Error | null): Full | null {
$mol_wire_solid()
if (cache) {
// this.$.$gd_kit_batch.promises[id]?.done(cache)
return cache as Full
}
if (patch && this.$.$gd_kit_batch.level > 0) {
const unit = this.data_async(id, patch)
const promise = unit
.then(val => $mol_wire_async(this).data(id, null, val))
.catch(err => $mol_wire_async(this).data(id, null, err))
this.$.$gd_kit_batch.promises[id] = Object.assign(promise, unit)
const prev = $mol_wire_probe(() => this.data(id))
const res = {
...prev,
...patch as unknown as Full
}
return res
}
return $mol_wire_sync(this).data_async(id, patch)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment