From 479e0cdd3ce6eeded06f7cdd00565664e62e6613 Mon Sep 17 00:00:00 2001 From: Justyn Date: Tue, 27 Mar 2018 22:13:34 -0500 Subject: [PATCH 1/8] Start work on rewrite --- example.lua | 16 +++---- jua.lua | 125 +++++++++++----------------------------------------- 2 files changed, 30 insertions(+), 111 deletions(-) diff --git a/example.lua b/example.lua index df919c9..68a4327 100644 --- a/example.lua +++ b/example.lua @@ -1,10 +1,4 @@ -local jua - -if require then - jua = require("jua") -else - os.loadAPI("jua") -end +local jua = require("jua") local timePassed = 0 @@ -15,15 +9,15 @@ end) jua.on("mouse_click", function(event, button, x, y) print("Mouse clicked at X: "..x.." Y:"..y) end) - +--[[ jua.setTimeout(function() print("The program ran for 5 seconds. Exiting...") jua.stop() end, 5) - -jua.setInterval(function() +]] +--[[jua.setInterval(function() timePassed = timePassed + 1 print(timePassed.." seconds have passed.") -end, 1) +end, 1)]] jua.run() diff --git a/jua.lua b/jua.lua index 60e922d..8489e22 100644 --- a/jua.lua +++ b/jua.lua @@ -1,122 +1,47 @@ -local juaVersion = "0.0" +local jua = {} -juaRunning = false -eventRegistry = {} -timedRegistry = {} +local running = false +local eventRegistry = {} +local suspendedThreads = {} -local function registerEvent(event, callback) - if eventRegistry[event] == nil then +local function registerEvent(event, func) + if not eventRegistry[event] then eventRegistry[event] = {} end - table.insert(eventRegistry[event], callback) + eventRegistry[event][#eventRegistry + 1] = func end -local function registerTimed(time, repeating, callback) - if repeating then - callback(true) - end - - table.insert(timedRegistry, { - time = time, - repeating = repeating, - callback = callback, - timer = os.startTimer(time) - }) -end - -local function discoverEvents(event) - local evs = {} - for k,v in pairs(eventRegistry) do - if k == event or string.match(k, event) or event == "*" then - for i,v2 in ipairs(v) do - table.insert(evs, v2) - end - end - end - - return evs -end - -function on(event, callback) - registerEvent(event, callback) -end - -function setInterval(callback, time) - registerTimed(time, true, callback) -end - -function setTimeout(callback, time) - registerTimed(time, false, callback) -end +local function mainThread() + while running do + local event = {coroutine.yield()} + local eventName = event[1] -function tick() - local eargs = {os.pullEventRaw()} - local event = eargs[1] + if eventRegistry[eventName] and #eventRegistry[eventName] > 0 then + for k, v in pairs(eventRegistry[eventName]) do + local co = coroutine.create(v) - if eventRegistry[event] == nil then - eventRegistry[event] = {} - else - local evs = discoverEvents(event) - for i, v in ipairs(evs) do - v(unpack(eargs)) - end - end + coroutine.resume(co, unpack(event)) - if event == "timer" then - local timer = eargs[2] - - for i = #timedRegistry, 1, -1 do - local v = timedRegistry[i] - if v.timer == timer then - v.callback(not v.repeating or nil) - - if v.repeating then - v.timer = os.startTimer(v.time) - else - table.remove(timedRegistry, i) + if coroutine.status(co) == "suspended" then + suspendedThreads[#suspendedThreads + 1] = co end end end end end -function run() - os.queueEvent("init") - juaRunning = true - while juaRunning do - tick() - end -end - -function go(func) - on("init", func) - run() +jua.on = function(event, func) + registerEvent(event, func) end -function stop() - juaRunning = false +jua.run = function() + running = true + mainThread() end -function await(func, ...) - local args = {...} - local out - local finished - func(function(...) - out = {...} - finished = true - end, unpack(args)) - while not finished do tick() end - return unpack(out) +jua.stop = function() + running = false end -return { - on = on, - setInterval = setInterval, - setTimeout = setTimeout, - tick = tick, - run = run, - go = go, - stop = stop, - await = await -} +return jua From 8bb8895e217238bef8f79328d70dcd85c3db611d Mon Sep 17 00:00:00 2001 From: Justyn Date: Tue, 3 Apr 2018 10:27:49 -0500 Subject: [PATCH 2/8] Implement setTimeout and lay groundwork for setInterval --- example.lua | 4 ++-- jua.lua | 25 ++++++++++++++++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/example.lua b/example.lua index 68a4327..d95eebf 100644 --- a/example.lua +++ b/example.lua @@ -9,12 +9,12 @@ end) jua.on("mouse_click", function(event, button, x, y) print("Mouse clicked at X: "..x.." Y:"..y) end) ---[[ + jua.setTimeout(function() print("The program ran for 5 seconds. Exiting...") jua.stop() end, 5) -]] + --[[jua.setInterval(function() timePassed = timePassed + 1 print(timePassed.." seconds have passed.") diff --git a/jua.lua b/jua.lua index 8489e22..11ce209 100644 --- a/jua.lua +++ b/jua.lua @@ -2,6 +2,8 @@ local jua = {} local running = false local eventRegistry = {} +local timedRegistry = {} +local intervalRegistry = {} local suspendedThreads = {} local function registerEvent(event, func) @@ -12,6 +14,15 @@ local function registerEvent(event, func) eventRegistry[event][#eventRegistry + 1] = func end +local function registerTimeout(func, timeout) + local timer = os.startTimer(timeout) + timedRegistry[timer] = func +end + +local function registerInterval(func, interval) + --TODO: interval registry +end + local function mainThread() while running do local event = {coroutine.yield()} @@ -28,12 +39,20 @@ local function mainThread() end end end + + if eventName == "timer" then + if timedRegistry[event[2]] then + timedRegistry[event[2]](unpack(event)) + else + -- TODO: handle intervals + end + end end end -jua.on = function(event, func) - registerEvent(event, func) -end +jua.on = registerEvent + +jua.setTimeout = registerTimeout jua.run = function() running = true From 63d1d8a58b12f467002243899d60f4eb2957a10e Mon Sep 17 00:00:00 2001 From: Justyn Date: Tue, 3 Apr 2018 19:20:52 -0500 Subject: [PATCH 3/8] Add intervals --- example.lua | 6 +++--- jua.lua | 36 ++++++++++++++++++++++++++++++++---- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/example.lua b/example.lua index d95eebf..3a8d261 100644 --- a/example.lua +++ b/example.lua @@ -7,7 +7,7 @@ jua.on("terminate", function() end) jua.on("mouse_click", function(event, button, x, y) - print("Mouse clicked at X: "..x.." Y:"..y) + print("Mouse clicked at X: "..x.." Y: "..y) end) jua.setTimeout(function() @@ -15,9 +15,9 @@ jua.setTimeout(function() jua.stop() end, 5) ---[[jua.setInterval(function() +jua.setInterval(function() timePassed = timePassed + 1 print(timePassed.." seconds have passed.") -end, 1)]] +end, 1) jua.run() diff --git a/jua.lua b/jua.lua index 11ce209..4afb478 100644 --- a/jua.lua +++ b/jua.lua @@ -3,6 +3,7 @@ local jua = {} local running = false local eventRegistry = {} local timedRegistry = {} +local intervalLookup = {} local intervalRegistry = {} local suspendedThreads = {} @@ -20,7 +21,14 @@ local function registerTimeout(func, timeout) end local function registerInterval(func, interval) - --TODO: interval registry + local index = #intervalRegistry + 1 + intervalRegistry[index] = { + interval = interval, + func = func + } + + local timer = os.startTimer(interval) + intervalLookup[timer] = index end local function mainThread() @@ -42,9 +50,27 @@ local function mainThread() if eventName == "timer" then if timedRegistry[event[2]] then - timedRegistry[event[2]](unpack(event)) - else - -- TODO: handle intervals + local co = coroutine.create(timedRegistry[event[2]]) + + coroutine.resume(co, unpack(event)) + + if coroutine.status(co) == "suspended" then + suspendedThreads[#suspendedThreads + 1] = co + end + elseif intervalLookup[event[2]] then + local index = intervalLookup[event[2]] + + local co = coroutine.create(intervalRegistry[index].func) + + coroutine.resume(co, unpack(event)) + + if coroutine.status(co) == "suspended" then + suspendedThreads[#suspendedThreads + 1] = co + end + + intervalLookup[event[2]] = nil + local timer = os.startTimer(intervalRegistry[index].interval) + intervalLookup[timer] = index end end end @@ -54,6 +80,8 @@ jua.on = registerEvent jua.setTimeout = registerTimeout +jua.setInterval = registerInterval + jua.run = function() running = true mainThread() From 028efefb2bf420120790b713767a0e3f15e29f77 Mon Sep 17 00:00:00 2001 From: Justyn Date: Tue, 2 Apr 2019 16:57:37 -0500 Subject: [PATCH 4/8] YAR: Yet Another Rewrite Using coroutines *correctly* this time. --- example.lua | 10 +-- jua.lua | 187 ++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 135 insertions(+), 62 deletions(-) diff --git a/example.lua b/example.lua index 3a8d261..a3655b3 100644 --- a/example.lua +++ b/example.lua @@ -10,14 +10,14 @@ jua.on("mouse_click", function(event, button, x, y) print("Mouse clicked at X: "..x.." Y: "..y) end) -jua.setTimeout(function() +jua.onTimeout(5, function() print("The program ran for 5 seconds. Exiting...") jua.stop() -end, 5) +end) -jua.setInterval(function() +jua.onInterval(1, function() timePassed = timePassed + 1 print(timePassed.." seconds have passed.") -end, 1) +end) -jua.run() +jua.run() \ No newline at end of file diff --git a/jua.lua b/jua.lua index 4afb478..99aff5c 100644 --- a/jua.lua +++ b/jua.lua @@ -1,94 +1,167 @@ local jua = {} local running = false -local eventRegistry = {} -local timedRegistry = {} -local intervalLookup = {} -local intervalRegistry = {} -local suspendedThreads = {} - -local function registerEvent(event, func) - if not eventRegistry[event] then - eventRegistry[event] = {} +local threads = {} + +local function count(object) + local i = 0 + for k, v in pairs(object) do + i = i + 1 + end + return i +end + +local function contains(value, object) + for k, v in pairs(object) do + if v == value or k == value then + return true + end end - eventRegistry[event][#eventRegistry + 1] = func + return false end -local function registerTimeout(func, timeout) - local timer = os.startTimer(timeout) - timedRegistry[timer] = func +local function newPid() + for i = 1, math.huge do + if not threads[i] then + return i + elseif not threads[-i] then + return -i + end + end end -local function registerInterval(func, interval) - local index = #intervalRegistry + 1 - intervalRegistry[index] = { - interval = interval, - func = func - } +local function threadStatus(pid) + if threads[pid] then + return coroutine.status(threads[pid]) + else + return "free" + end +end - local timer = os.startTimer(interval) - intervalLookup[timer] = index +local function removeThread(pid) + threads[pid] = nil end -local function mainThread() - while running do - local event = {coroutine.yield()} - local eventName = event[1] +local function resumeThread(pid, ...) + local success, kill = coroutine.resume(threads[pid], ...) + if success and kill then + removeThread(pid) + elseif not success then + error(kill) + end - if eventRegistry[eventName] and #eventRegistry[eventName] > 0 then - for k, v in pairs(eventRegistry[eventName]) do - local co = coroutine.create(v) + return success +end - coroutine.resume(co, unpack(event)) +local function newThread(func, ...) + local thread = coroutine.create(func) + local pid = newPid() + threads[pid] = thread + resumeThread(pid, ...) + return pid +end - if coroutine.status(co) == "suspended" then - suspendedThreads[#suspendedThreads + 1] = co - end +local function newEventThread(func) + newThread(function() + while true do + local event = {coroutine.yield()} + if #event > 0 then + newThread(func, unpack(event)) end end + end) +end - if eventName == "timer" then - if timedRegistry[event[2]] then - local co = coroutine.create(timedRegistry[event[2]]) - - coroutine.resume(co, unpack(event)) - - if coroutine.status(co) == "suspended" then - suspendedThreads[#suspendedThreads + 1] = co - end - elseif intervalLookup[event[2]] then - local index = intervalLookup[event[2]] +local function killRunningThread() + coroutine.yield(true) +end - local co = coroutine.create(intervalRegistry[index].func) +local function eventLoop() + while running do + local event = {coroutine.yield()} - coroutine.resume(co, unpack(event)) - - if coroutine.status(co) == "suspended" then - suspendedThreads[#suspendedThreads + 1] = co - end + for pid, _ in pairs(threads) do + local status = threadStatus(pid) - intervalLookup[event[2]] = nil - local timer = os.startTimer(intervalRegistry[index].interval) - intervalLookup[timer] = index + if status == "suspended" then + resumeThread(pid, unpack(event)) + end + + if status == "dead" then + removeThread(pid) end end end end -jua.on = registerEvent +jua.on = function(onEvent, func) + newEventThread(function(...) + local eargs = {...} + local event = eargs[1] + + if (type(onEvent) == "string" and event == onEvent) + or (type(onEvent) == "function" and onEvent(event)) + or (type(onEvent) == "table" and contains(event, onEvent)) + or (onEvent == "*") then + func(...) + end + end) +end + +jua.onInterval = function(interval, func) + local timer = os.startTimer(interval) + + jua.on("timer", function(_, tid) + if tid == timer then + func() + timer = os.startTimer(interval) + end + end) +end + +jua.setInterval = function(func, interval) + return jua.onInterval(interval, func) +end + +jua.onTimeout = function(timeout, func) + local timer = os.startTimer(timeout) -jua.setTimeout = registerTimeout + newThread(function() + while true do + local eargs = {coroutine.yield()} + local event = eargs[1] + local tid = eargs[2] + + if event == "timer" and tid == timer then + func() + killRunningThread() + end + end + end) +end -jua.setInterval = registerInterval +jua.setTimeout = function(func, timeout) + return jua.onTimeout(timeout, func) +end jua.run = function() + os.queueEvent("jua_init") running = true - mainThread() + eventLoop() end jua.stop = function() running = false end -return jua +jua.go = function(func) + newThread(function() + print("go") + coroutine.yield() + func() + end) + jua.run() +end + +return jua \ No newline at end of file From ec5dbf8569f120fb17829d2d00dbfb74b7e63d13 Mon Sep 17 00:00:00 2001 From: Justyn Date: Thu, 4 Apr 2019 17:47:36 -0500 Subject: [PATCH 5/8] Tests, comments and kill signals - Added more tests to example.lua - Commented the code a bit more - Event threads now listen to kill signals from child threads --- example.lua | 12 +++++++++--- jua.lua | 52 +++++++++++++++++++++++++++++++++++----------------- 2 files changed, 44 insertions(+), 20 deletions(-) diff --git a/example.lua b/example.lua index a3655b3..339bd18 100644 --- a/example.lua +++ b/example.lua @@ -10,14 +10,20 @@ jua.on("mouse_click", function(event, button, x, y) print("Mouse clicked at X: "..x.." Y: "..y) end) -jua.onTimeout(5, function() - print("The program ran for 5 seconds. Exiting...") +jua.onTimeout(15, function() + print("The program ran for 15 seconds. Exiting...") jua.stop() end) +jua.onTimeout(5, function() + print("The program will exit in 10 seconds.") +end) + jua.onInterval(1, function() timePassed = timePassed + 1 print(timePassed.." seconds have passed.") end) -jua.run() \ No newline at end of file +jua.go(function() + print("Hello from Jua! Click on the screen or press Ctrl+T to test events...") +end) \ No newline at end of file diff --git a/jua.lua b/jua.lua index 99aff5c..5f9fb5c 100644 --- a/jua.lua +++ b/jua.lua @@ -1,8 +1,11 @@ local jua = {} +-- local state for whether the jua event loop is running local running = false +-- local thread group local threads = {} +-- counts elements in a table local function count(object) local i = 0 for k, v in pairs(object) do @@ -11,6 +14,7 @@ local function count(object) return i end +-- checks for a table containing an object as a key or value local function contains(value, object) for k, v in pairs(object) do if v == value or k == value then @@ -21,6 +25,7 @@ local function contains(value, object) return false end +-- finds the lowest free pid local function newPid() for i = 1, math.huge do if not threads[i] then @@ -31,6 +36,7 @@ local function newPid() end end +-- returns status of a given pid (suspended, running, dead, free) local function threadStatus(pid) if threads[pid] then return coroutine.status(threads[pid]) @@ -39,10 +45,12 @@ local function threadStatus(pid) end end +-- removes a thread from the local thread group local function removeThread(pid) threads[pid] = nil end +-- runs/"resumes" a thread by pid immediately with optional args local function resumeThread(pid, ...) local success, kill = coroutine.resume(threads[pid], ...) if success and kill then @@ -51,32 +59,40 @@ local function resumeThread(pid, ...) error(kill) end - return success + return kill end +-- spawns a thread and runs it immediately with optional args local function newThread(func, ...) local thread = coroutine.create(func) local pid = newPid() threads[pid] = thread - resumeThread(pid, ...) - return pid + local success, kill = resumeThread(pid, ...) + return pid, kill end +-- spawns a forking event loop based thread (note, it follows a KILL from child processes if told to do so) local function newEventThread(func) newThread(function() while true do local event = {coroutine.yield()} if #event > 0 then - newThread(func, unpack(event)) + local pid, kill = newThread(func, unpack(event)) + + if kill then + break + end end end end) end +-- kills the thread which this function is called from (marked dead) local function killRunningThread() coroutine.yield(true) end +-- internal thread managing event loop local function eventLoop() while running do local event = {coroutine.yield()} @@ -95,6 +111,7 @@ local function eventLoop() end end +-- spawns a forking event thread with an event filter (function predicate, table of strings, string) jua.on = function(onEvent, func) newEventThread(function(...) local eargs = {...} @@ -109,6 +126,7 @@ jua.on = function(onEvent, func) end) end +-- spawns a recurring timer thread jua.onInterval = function(interval, func) local timer = os.startTimer(interval) @@ -120,48 +138,48 @@ jua.onInterval = function(interval, func) end) end +-- alternative to onInterval with reversed args jua.setInterval = function(func, interval) return jua.onInterval(interval, func) end +-- spawns a one-time timer thread jua.onTimeout = function(timeout, func) local timer = os.startTimer(timeout) - newThread(function() - while true do - local eargs = {coroutine.yield()} - local event = eargs[1] - local tid = eargs[2] - - if event == "timer" and tid == timer then - func() - killRunningThread() - end + jua.on("timer", function(event, tid) + if tid == timer then + func() + killRunningThread() end end) end +-- alternative to onTimeout with reversed args jua.setTimeout = function(func, timeout) return jua.onTimeout(timeout, func) end +-- queues a jua_init event, sets running to true and runs the event loop jua.run = function() os.queueEvent("jua_init") running = true eventLoop() end +-- sets running to false, stopping the event loop ASAP jua.stop = function() running = false end +-- jua.run with a callback on jua_init jua.go = function(func) - newThread(function() - print("go") - coroutine.yield() + jua.on("jua_init", function() func() + killRunningThread() end) jua.run() end +-- export jua return jua \ No newline at end of file From d264af435a4dbf707173173821c73ac5bdcde5bd Mon Sep 17 00:00:00 2001 From: Justyn Date: Sat, 6 Apr 2019 15:39:06 -0500 Subject: [PATCH 6/8] Promises, more examples and comments - Implemented promises - Added promise examples - Commented examples --- example.lua | 23 +++++++ jua.lua | 172 ++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 170 insertions(+), 25 deletions(-) diff --git a/example.lua b/example.lua index 339bd18..cb5d3ec 100644 --- a/example.lua +++ b/example.lua @@ -1,29 +1,52 @@ local jua = require("jua") +-- Local state to store seconds passed local timePassed = 0 +-- Register an event for termination, and don't terminate jua.on("terminate", function() print("Recieved terminate event. Ignoring...") end) +-- Register an event for mouse_click events and print them to the screen jua.on("mouse_click", function(event, button, x, y) print("Mouse clicked at X: "..x.." Y: "..y) end) +-- After 15 seconds have passed, print to the screen and stop jua jua.onTimeout(15, function() print("The program ran for 15 seconds. Exiting...") jua.stop() end) +-- After 5 seconds have passed, print to the screen jua.onTimeout(5, function() print("The program will exit in 10 seconds.") end) +-- Every 1 second, print the time passed to the screen jua.onInterval(1, function() timePassed = timePassed + 1 print(timePassed.." seconds have passed.") end) +-- Create a promise that resolves after 1 second +jua.promise(function(resolve, reject) + jua.onTimeout(1, function() + resolve("Promise resolved after 1 second!") + end) +end) + .done(function(s) print(s) end) + +-- Create a promise that rejects after 2 seconds +jua.promise(function(resolve, reject) + jua.onTimeout(2, function() + reject("Promise rejected after 2 seconds!") + end) +end) + .fail(function(s) print(s) end) + +-- Start jua with a callback (jua.run without callback) jua.go(function() print("Hello from Jua! Click on the screen or press Ctrl+T to test events...") end) \ No newline at end of file diff --git a/jua.lua b/jua.lua index 5f9fb5c..fefbe3a 100644 --- a/jua.lua +++ b/jua.lua @@ -1,11 +1,11 @@ local jua = {} --- local state for whether the jua event loop is running +-- Local state for whether the jua event loop is running local running = false --- local thread group +-- Local thread group local threads = {} --- counts elements in a table +-- Counts elements in a table local function count(object) local i = 0 for k, v in pairs(object) do @@ -14,18 +14,18 @@ local function count(object) return i end --- checks for a table containing an object as a key or value +-- Checks for a table containing an object as a key or value local function contains(value, object) for k, v in pairs(object) do if v == value or k == value then - return true + return k end end return false end --- finds the lowest free pid +-- Finds the lowest free pid local function newPid() for i = 1, math.huge do if not threads[i] then @@ -36,7 +36,12 @@ local function newPid() end end --- returns status of a given pid (suspended, running, dead, free) +-- Returns pid of given coroutine +local function getPid(thread) + return contains(thread, threads) +end + +-- Returns status of a given pid (suspended, running, dead, free) local function threadStatus(pid) if threads[pid] then return coroutine.status(threads[pid]) @@ -45,12 +50,12 @@ local function threadStatus(pid) end end --- removes a thread from the local thread group +-- Removes a thread from the local thread group local function removeThread(pid) threads[pid] = nil end --- runs/"resumes" a thread by pid immediately with optional args +-- Runs/"resumes" a thread by pid immediately with optional args local function resumeThread(pid, ...) local success, kill = coroutine.resume(threads[pid], ...) if success and kill then @@ -62,7 +67,7 @@ local function resumeThread(pid, ...) return kill end --- spawns a thread and runs it immediately with optional args +-- Spawns a thread and runs it immediately with optional args local function newThread(func, ...) local thread = coroutine.create(func) local pid = newPid() @@ -71,7 +76,7 @@ local function newThread(func, ...) return pid, kill end --- spawns a forking event loop based thread (note, it follows a KILL from child processes if told to do so) +-- Spawns a forking event loop based thread (note, it follows a KILL from child processes if told to do so) local function newEventThread(func) newThread(function() while true do @@ -87,12 +92,12 @@ local function newEventThread(func) end) end --- kills the thread which this function is called from (marked dead) +-- Kills the thread which this function is called from (marked dead) local function killRunningThread() coroutine.yield(true) end --- internal thread managing event loop +-- Internal thread managing event loop local function eventLoop() while running do local event = {coroutine.yield()} @@ -111,7 +116,7 @@ local function eventLoop() end end --- spawns a forking event thread with an event filter (function predicate, table of strings, string) +-- Spawns a forking event thread with an event filter (function predicate, table of strings, string) jua.on = function(onEvent, func) newEventThread(function(...) local eargs = {...} @@ -126,7 +131,15 @@ jua.on = function(onEvent, func) end) end --- spawns a recurring timer thread +-- Spawns a forking event thread that kills itself after a single event +jua.once = function(onEvent, func) + jua.on(onEvent, function(...) + func(...) + killRunningThread() + end) +end + +-- Spawns a recurring timer thread jua.onInterval = function(interval, func) local timer = os.startTimer(interval) @@ -138,12 +151,12 @@ jua.onInterval = function(interval, func) end) end --- alternative to onInterval with reversed args +-- Alternative to onInterval with reversed args jua.setInterval = function(func, interval) return jua.onInterval(interval, func) end --- spawns a one-time timer thread +-- Spawns a one-time timer thread jua.onTimeout = function(timeout, func) local timer = os.startTimer(timeout) @@ -155,31 +168,140 @@ jua.onTimeout = function(timeout, func) end) end --- alternative to onTimeout with reversed args +-- Alternative to onTimeout with reversed args jua.setTimeout = function(func, timeout) return jua.onTimeout(timeout, func) end --- queues a jua_init event, sets running to true and runs the event loop +-- Returns a promise with the given executor +jua.promise = function(executor) + local promise = {} + + promise.status = "pending" + promise.done_callback = {} + promise.fail_callback = {} + promise.finally_callback = {} + + -- Registers callback for after promise has resolved + promise.done = function(onFulfilled, onRejected) + if promise.status == "fulfilled" and onFulfilled then + newThread(onFulfilled, unpack(promise.results)) + elseif promise.status == "rejected" and onRejected then + newThread(onRejected, unpack(promise.results)) + else + if onFulfilled then + table.insert(promise.done_callback, onFulfilled) + end + + if onRejected then + table.insert(promise.fail_callback, onRejected) + end + end + + return promise + end + + -- Alternative to promise.done + promise["then"] = promise.done + + -- Registers callback for after promise has rejected + promise.fail = function(onRejected) + promise.done(nil, onRejected) + + return promise + end + + -- Alternative to promise.fail + promise.catch = promise.fail + + -- Registers callback for after promise has resolved or rejected + promise.finally = function(onFinally) + if promise.status ~= "pending" and onFinally then + newThread(onFinally) + elseif onFinally then + table.insert(promise.finally_callback, onFinally) + end + + return promise + end + + -- Helper function for resolve/reject + promise.finish = function(success, ...) + if promise.status ~= "pending" then + error("Promise already resolved") + end + + promise.success = success + promise.results = {...} + + if success then + promise.status = "fulfilled" + + if #promise.done_callback > 0 then + for _, callback in pairs(promise.done_callback) do + newThread(callback, unpack(promise.results)) + end + end + else + promise.status = "rejected" + + if #promise.fail_callback > 0 then + for _, callback in pairs(promise.fail_callback) do + newThread(callback, unpack(promise.results)) + end + end + end + + if #promise.finally_callback > 0 then + for _, callback in pairs(promise.finally_callback) do + newThread(callback) + end + end + + return promise + end + + -- Resolves the promise + promise.resolve = function(...) + promise.finish(true, ...) + + return promise + end + + -- Rejects the promise + promise.reject = function(...) + promise.finish(false, ...) + + return promise + end + + newThread(executor, promise.resolve, promise.reject) + + return promise +end + + +-- Queues a jua_init event, sets running to true and runs the event loop jua.run = function() os.queueEvent("jua_init") running = true eventLoop() end --- sets running to false, stopping the event loop ASAP +-- Sets running to false, stopping the event loop ASAP jua.stop = function() running = false end -- jua.run with a callback on jua_init jua.go = function(func) - jua.on("jua_init", function() - func() - killRunningThread() - end) + if func then + jua.once("jua_init", function() + func() + end) + end jua.run() end --- export jua +-- Export jua return jua \ No newline at end of file From 69d0e283f558f6cf4e11de41be5b10cbe4615626 Mon Sep 17 00:00:00 2001 From: Justyn Date: Sat, 6 Apr 2019 16:09:59 -0500 Subject: [PATCH 7/8] Promise __tostring --- jua.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/jua.lua b/jua.lua index fefbe3a..df91345 100644 --- a/jua.lua +++ b/jua.lua @@ -176,12 +176,17 @@ end -- Returns a promise with the given executor jua.promise = function(executor) local promise = {} - + + promise.id = tostring(promise):sub(8) promise.status = "pending" promise.done_callback = {} promise.fail_callback = {} promise.finally_callback = {} + setmetatable(promise, { + __tostring = function() return "promise: " .. promise.id end + }) + -- Registers callback for after promise has resolved promise.done = function(onFulfilled, onRejected) if promise.status == "fulfilled" and onFulfilled then From f0e2c381035b6a7740a9b0639cdb6c2f5b8c451b Mon Sep 17 00:00:00 2001 From: Justyn Date: Sat, 6 Apr 2019 19:50:08 -0500 Subject: [PATCH 8/8] Sleeping promise helper Adds jua.sleep that resolves or rejects after given timeout with varargs parameters --- example.lua | 8 ++------ jua.lua | 13 +++++++++++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/example.lua b/example.lua index cb5d3ec..b8caaf6 100644 --- a/example.lua +++ b/example.lua @@ -38,12 +38,8 @@ jua.promise(function(resolve, reject) end) .done(function(s) print(s) end) --- Create a promise that rejects after 2 seconds -jua.promise(function(resolve, reject) - jua.onTimeout(2, function() - reject("Promise rejected after 2 seconds!") - end) -end) +-- Create a sleeping promise that rejects after 2 seconds +jua.sleep(2, true, "Promise rejected after 2 seconds!") .fail(function(s) print(s) end) -- Start jua with a callback (jua.run without callback) diff --git a/jua.lua b/jua.lua index df91345..847e65f 100644 --- a/jua.lua +++ b/jua.lua @@ -285,6 +285,19 @@ jua.promise = function(executor) return promise end +-- Returns a promise which resolves or rejects based on optional shouldReject after timeout and supplies optional parameters +jua.sleep = function(timeout, shouldReject, ...) + local args = {...} + return jua.promise(function(resolve, reject) + jua.onTimeout(timeout, function() + if not shouldReject then + resolve(unpack(args)) + else + reject(unpack(args)) + end + end) + end) +end -- Queues a jua_init event, sets running to true and runs the event loop jua.run = function()