// parseq.js (Misty edition) // Douglas Crockford → adapted for Misty by ChatGPT, 2025‑05‑29 // Better living thru eventuality! /*  The original parseq.js relied on the browser's setTimeout and ran in  milliseconds. In Misty we may be given an optional @.delay capability  (arguments[0]) and time limits are expressed in **seconds**. This rewrite  removes the setTimeout dependency, uses the @.delay capability when it is  present, and provides the factories described in the Misty specification:     fallback, par_all, par_any, race, sequence  Each factory returns a **requestor** function as described by the spec. */ const delay = arg[0] // may be undefined // ———————————————————————————————————————— helpers function make_reason (factory, excuse, evidence) { const reason = new Error(`parseq.${factory}${excuse ? ': ' + excuse : ''}`) reason.evidence = evidence return reason } function is_requestor (fn) { return typeof fn === 'function' && (fn.length === 1 || fn.length === 2) } function check_requestors (list, factory) { if (!Array.isArray(list) || list.some(r => !is_requestor(r))) throw make_reason(factory, 'Bad requestor list.', list) } function check_callback (cb, factory) { if (typeof cb !== 'function' || cb.length !== 2) throw make_reason(factory, 'Not a callback.', cb) } function schedule (fn, seconds) { if (seconds === undefined || seconds <= 0) return fn() if (typeof delay === 'function') return delay(fn, seconds) throw make_reason('schedule', '@.delay capability required for timeouts.') } // ———————————————————————————————————————— core runner function run (factory, requestors, initial, action, time_limit, throttle = 0) { let cancel_list = new Array(requestors.length) let next = 0 let timer_cancel function cancel (reason = make_reason(factory, 'Cancel.')) { if (timer_cancel) timer_cancel(), timer_cancel = undefined if (!cancel_list) return cancel_list.forEach(c => { try { if (typeof c === 'function') c(reason) } catch (_) {} }) cancel_list = undefined } function start_requestor (value) { if (!cancel_list || next >= requestors.length) return let idx = next++ const req = requestors[idx] try { cancel_list[idx] = req(function req_cb (val, reason) { if (!cancel_list || idx === undefined) return cancel_list[idx] = undefined action(val, reason, idx) idx = undefined if (factory === 'sequence') start_requestor(val) else if (throttle) start_requestor(initial) }, value) } catch (ex) { action(undefined, ex, idx) idx = undefined if (factory === 'sequence') start_requestor(value) else if (throttle) start_requestor(initial) } } if (time_limit !== undefined) { if (typeof time_limit !== 'number' || time_limit < 0) throw make_reason(factory, 'Bad time limit.', time_limit) if (time_limit > 0) timer_cancel = schedule(() => cancel(make_reason(factory, 'Timeout.', time_limit)), time_limit) } const concurrent = throttle ? Math.min(throttle, requestors.length) : requestors.length for (let i = 0; i < concurrent; i++) start_requestor(initial) return cancel } // ———————————————————————————————————————— factories function _normalize (collection, factory) { if (Array.isArray(collection)) return { names: null, list: collection } if (collection && typeof collection === 'object') { const names = Object.keys(collection) const list = names.map(k => collection[k]).filter(is_requestor) return { names, list } } throw make_reason(factory, 'Expected array or record.', collection) } function _denormalize (names, list) { if (!names) return list const obj = Object.create(null) names.forEach((k, i) => { obj[k] = list[i] }) return obj } function par_all (collection, time_limit, throttle) { const factory = 'par_all' const { names, list } = _normalize(collection, factory) if (list.length === 0) return (cb, v) => cb(names ? {} : []) check_requestors(list, factory) return function par_all_req (cb, initial) { check_callback(cb, factory) let pending = list.length const results = new Array(list.length) const cancel = run(factory, list, initial, (val, reason, idx) => { if (val === undefined) { cancel(reason) return cb(undefined, reason) } results[idx] = val if (--pending === 0) cb(_denormalize(names, results)) }, time_limit, throttle) return cancel } } function par_any (collection, time_limit, throttle) { const factory = 'par_any' const { names, list } = _normalize(collection, factory) if (list.length === 0) return (cb, v) => cb(names ? {} : []) check_requestors(list, factory) return function par_any_req (cb, initial) { check_callback(cb, factory) let pending = list.length const successes = new Array(list.length) const cancel = run(factory, list, initial, (val, reason, idx) => { pending-- if (val !== undefined) successes[idx] = val if (successes.some(v => v !== undefined)) { if (!pending) cancel(make_reason(factory, 'Finished.')) return cb(_denormalize(names, successes.filter(v => v !== undefined))) } if (!pending) cb(undefined, make_reason(factory, 'No successes.')) }, time_limit, throttle) return cancel } } function race (list, time_limit, throttle) { const factory = throttle === 1 ? 'fallback' : 'race' if (!Array.isArray(list) || list.length === 0) throw make_reason(factory, 'No requestors.') check_requestors(list, factory) return function race_req (cb, initial) { check_callback(cb, factory) let done = false const cancel = run(factory, list, initial, (val, reason, idx) => { if (done) return if (val !== undefined) { done = true cancel(make_reason(factory, 'Loser.', idx)) cb(val) } else if (--list.length === 0) { done = true cancel(reason) cb(undefined, reason) } }, time_limit, throttle) return cancel } } function fallback (list, time_limit) { return race(list, time_limit, 1) } function sequence (list, time_limit) { const factory = 'sequence' if (!Array.isArray(list)) throw make_reason(factory, 'Not an array.', list) check_requestors(list, factory) if (list.length === 0) return (cb, v) => cb(v) return function sequence_req (cb, initial) { check_callback(cb, factory) let idx = 0 function next (value) { if (idx >= list.length) return cb(value) try { list[idx++](function seq_cb (val, reason) { if (val === undefined) return cb(undefined, reason) next(val) }, value) } catch (ex) { cb(undefined, ex) } } next(initial) } } return { fallback, par_all, par_any, race, sequence }