UNPKG

decurse

Version:

An abstraction over continuation-passing and trampolining to write recursive functions that don't exceed the maximum call stack size.

62 lines (61 loc) 1.53 kB
class OffStack { #resolved = !1 #value = void 0 #dependents = [] #decurse constructor(decurse, executor) { this.#decurse = decurse decurse.pending.push(() => executor(value => this.#resolve(value))) decurse.autoPump && pumpAll(decurse) } #resolve(value) { if (!this.#resolved) { if (value instanceof OffStack) { if (!value.#resolved) { value.#dependents.push(value => this.#resolve(value)) return } value = value.#value } this.#resolved = !0 this.#value = value for (const dependent of this.#dependents) this.#decurse.pending.push(() => dependent(this.#value)) this.#decurse.autoPump && pumpAll(this.#decurse) } } then(callback) { return new OffStack(this.#decurse, resolve => { this.#resolved ? this.#decurse.pending.push(() => resolve(callback(this.#value))) : this.#dependents.push(value => resolve(callback(value))) }) } } function pump(decurse) { if (!decurse.active) { decurse.active = !0 try { decurse.pending.length && decurse.pending.pop()() } finally { decurse.active = !1 } } } function pumpAll(decurse) { if (!decurse.active) { decurse.active = !0 try { for (; decurse.pending.length; ) decurse.pending.pop()() } finally { decurse.active = !1 } } } function makeDecurse({ autoPump = !0 } = {}) { const decurse = callback => new OffStack(decurse, resolve => resolve(callback())) decurse.pending = [] decurse.active = !1 decurse.autoPump = autoPump return decurse } export { OffStack, makeDecurse, pump, pumpAll }