Last active
October 13, 2023 08:57
-
-
Save zerkalica/dd2a7aa7a5fed51f9aabca5a415987c6 to your computer and use it in GitHub Desktop.
batch
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
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