// 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. */ def delay = arg[0] // may be null // ———————————————————————————————————————— helpers function make_reason (factory, excuse, evidence) { def reason = new Error(`parseq.${factory}${excuse ? ': ' + excuse : ''}`) reason.evidence = evidence return reason } function is_requestor (fn) { return is_function(fn) && (fn.length == 1 || fn.length == 2) } function check_requestors (list, factory) { if (!is_array(list) || list.some(r => !is_requestor(r))) throw make_reason(factory, 'Bad requestor list.', list) } function check_callback (cb, factory) { if (!is_function(cb) || cb.length != 2) throw make_reason(factory, 'Not a callback.', cb) } function schedule (fn, seconds) { if (seconds == null || seconds <= 0) return fn() if (is_function(delay)) 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) { var cancel_list = new Array(requestors.length) var next = 0 var timer_cancel function cancel (reason = make_reason(factory, 'Cancel.')) { if (timer_cancel) timer_cancel(), timer_cancel = null if (!cancel_list) return cancel_list.forEach(c => { try { if (is_function(c)) c(reason) } catch (_) {} }) cancel_list = null } function start_requestor (value) { if (!cancel_list || next >= requestors.length) return var idx = next++ def req = requestors[idx] try { cancel_list[idx] = req(function req_cb (val, reason) { if (!cancel_list || idx == null) return cancel_list[idx] = null action(val, reason, idx) idx = null if (factory == 'sequence') start_requestor(val) else if (throttle) start_requestor(initial) }, value) } catch (ex) { action(null, ex, idx) idx = null if (factory == 'sequence') start_requestor(value) else if (throttle) start_requestor(initial) } } if (time_limit != null) { if (!is_number(time_limit) || 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) } def concurrent = throttle ? number.min(throttle, requestors.length) : requestors.length for (var i = 0; i < concurrent; i++) start_requestor(initial) return cancel } // ———————————————————————————————————————— factories function _normalize (collection, factory) { if (is_array(collection)) return { names: null, list: collection } if (collection && is_object(collection)) { def names = array(collection) def 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 def obj = meme(null) names.forEach((k, i) => { obj[k] = list[i] }) return obj } function par_all (collection, time_limit, throttle) { def factory = 'par_all' def { 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) var pending = list.length def results = new Array(list.length) def cancel = run(factory, list, initial, (val, reason, idx) => { if (val == null) { cancel(reason) return cb(null, reason) } results[idx] = val if (--pending == 0) cb(_denormalize(names, results)) }, time_limit, throttle) return cancel } } function par_any (collection, time_limit, throttle) { def factory = 'par_any' def { 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) var pending = list.length def successes = new Array(list.length) def cancel = run(factory, list, initial, (val, reason, idx) => { pending-- if (val != null) successes[idx] = val if (successes.some(v => v != null)) { if (!pending) cancel(make_reason(factory, 'Finished.')) return cb(_denormalize(names, successes.filter(v => v != null))) } if (!pending) cb(null, make_reason(factory, 'No successes.')) }, time_limit, throttle) return cancel } } function race (list, time_limit, throttle) { def factory = throttle == 1 ? 'fallback' : 'race' if (!is_array(list) || list.length == 0) throw make_reason(factory, 'No requestors.') check_requestors(list, factory) return function race_req (cb, initial) { check_callback(cb, factory) var done = false def cancel = run(factory, list, initial, (val, reason, idx) => { if (done) return if (val != null) { done = true cancel(make_reason(factory, 'Loser.', idx)) cb(val) } else if (--list.length == 0) { done = true cancel(reason) cb(null, reason) } }, time_limit, throttle) return cancel } } function fallback (list, time_limit) { return race(list, time_limit, 1) } function sequence (list, time_limit) { def factory = 'sequence' if (!is_array(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) var idx = 0 function next (value) { if (idx >= list.length) return cb(value) try { list[idx++](function seq_cb (val, reason) { if (val == null) return cb(null, reason) next(val) }, value) } catch (ex) { cb(null, ex) } } next(initial) } } return { fallback, par_all, par_any, race, sequence }