local task = require("@lune/task") local mod = require("../mod") local Signal = mod.signal type Signal = mod.Signal local option = require("option") type Option = option.Option local Some = option.Some local None = option.None local Future = {} export type Status = "initialized" | "running" | "cancelled" | "done" export type Future = typeof(Future) & { _fn: (set: (value: T) -> ()) -> T, _ret: T?, _thread: thread, _spawnEvt: Signal<()>, _retEvt: Signal, _status: Status, } function Future.new(fn: (...any) -> T, args: { any }) return setmetatable( { _thread = coroutine.create(function(spawnEvt: Signal<()>, retEvt: Signal) spawnEvt:Fire() local ret = fn(table.unpack(args)) retEvt:Fire(ret, "done") end), _spawnEvt = Signal.new(), _retEvt = Signal.new(), _status = "initialized", } :: Future, { __index = Future, } ) end function Future.poll(self: Future): (Status, Option) if self._status == "initialized" then self._retEvt:Connect(function(firedRet, status: Status) self._status = status self._ret = firedRet -- Cleanup coroutine.yield(self._thread) coroutine.close(self._thread) self._spawnEvt:DisconnectAll() self._retEvt:DisconnectAll() end) self._spawnEvt:Connect(function() self._status = "running" end) coroutine.resume(self._thread, self._spawnEvt, self._retEvt) end if self._status == "running" then -- Just wait a bit more for the signal to fire task.wait(0.01) end local retOpt = if self._ret == nil then None() else Some(self._ret) return self._status, retOpt end function Future.cancel(self: Future) self._retEvt:Fire(nil :: any, "cancelled") self._status = "cancelled" end