promise/sbrl: bugfix

it should work in most situations now.
This commit is contained in:
Starbeamrainbowlabs 2024-06-07 00:08:12 +01:00
parent 3ccea536c8
commit f654bde966
No known key found for this signature in database
GPG Key ID: 1BE5172E637709C2

@ -1,3 +1,5 @@
local inspect = require("worldeditadditions_core.utils.inspect")
--- Javascript Promises, implemented in Lua
-- In other words, a wrapper to manage asynchronous operations.
-- Due to limitations of Lua, while this Promise API is similar it isn't exactly the same as in JS. Most notably is that there is a .run() function you must call AFTER you call .then_() as many times as you need.
@ -15,92 +17,73 @@ Promise.__name = "Promise" -- A hack to allow identification in wea.inspect
function Promise.new(fn)
local result = {
state = "pending",
fns = { { then_ = fn } }
fns = { { fn_then_ = fn } }
}
setmetatable(result, Promise)
return result
end
-- NOTE: fn_catch support is NOT FULLY IMPLEMENTED yet!
-- Example: It doesn't work when calling non-async .then()s!
function Promise.then_(self, fn_then, fn_catch)
local step_item = {
then_ = fn_then,
}
if type(fn_catch) == "function" then
step_item.catch_ = fn_catch
end
table.insert(self.fns, step_item)
return self
------------------------------------------------------------------------
local do_unpack = unpack
if not do_unpack then do_unpack = table.unpack end
if not do_unpack then
error("Error: Failed to find unpack implementation")
end
--- Executes the function this Promise is wrapping all associated chained functions in sequence.
-- This is a separate method for portability, since Lua does not implement setTimeout which is required to ensure that if an non-async function is wrapped the parent function still has time to call .then_() before it finishes and the associated .then_()ed functions are attached correctly.
--
-- WARNING: If you call .then_() AFTER calling .run(), your .then_()ed function may not be called correctly!
-- @returns Promise The current promise, for daisy chaining purposes.
function Promise.run(self)
-- TODO update self.state here
do_run(promise, {}, 1, function(...)
end)
return self
end
local function do_run(promise, args, depth, origin_resolve)
-- print("DO_RUN promise", inspect(promise), "args", inspect(args), "depth", depth, "origin_resolve", origin_resolve)
---
-- 0: Termination condition
---
if #promise.fns == 0 then
promise.state = "fulfilled"
origin_resolve(table.unpack(args))
origin_resolve(do_unpack(args))
return
end
---
-- 1: Sort out inputs
---
if args == nil then args = {} end
local next = table.remove(promise.fns, 1)
local do_next_fn = function(args)
do_run(promise, args, depth + 1)
local do_next_fn = function(args_to_pass)
do_run(promise, args_to_pass, depth + 1, origin_resolve)
end
---
-- 2: Run teh function!
---
-- (if it's the 1st one in the sequence we treat it specially)
local results
if depth == 1 then
-- This function is the inner one of a promise already.
-- It has already recieved arguments, so it expected resolve/reject instead
next.then_(function(...) -- RESOLVE
next.fn_then_(function(...) -- RESOLVE
if getmetatable(arg[1]) == Promise then
promise.state = "rejected"
error("Error: Returning nested Promises is not currently supported.")
end
do_next_fn(args) -- Execute the next one in the sequence
do_next_fn(arg) -- Execute the next one in the sequence
end, function(...) -- REJECT
promise.state = "rejected"
if type(next.catch_) == "function" then
next.catch_(table.unpack(arg))
if type(next.fn_catch_) == "function" then
next.fn_catch_(do_unpack(arg))
else
error("Error: Promise rejected but no catch function present. TODO make this error message more useful and informative.")
error(
"Error: Promise rejected but no catch function present. TODO make this error message more useful and informative.")
-- If there's no catch registered, throw an error
end
end)
else
results = { next.then_(table.unpack(args)) }
results = { next.fn_then_(do_unpack(args)) }
---
-- 3: Handle the aftermath
---
@ -121,35 +104,67 @@ local function do_run(promise, args, depth, origin_resolve)
do_next_fn(results)
end
end
end
------------------------------------------------------------------------
-- NOTE: fn_catch support is NOT FULLY IMPLEMENTED yet!
-- Example: It doesn't work when calling non-async .then()s!
function Promise.then_(self, fn_then, fn_catch)
local step_item = {
fn_then_ = fn_then,
}
if type(fn_catch) == "function" then
step_item.fn_catch_ = fn_catch
end
table.insert(self.fns, step_item)
return self
end
--- Executes the function this Promise is wrapping all associated chained functions in sequence.
-- This is a separate method for portability, since Lua does not implement setTimeout which is required to ensure that if an non-async function is wrapped the parent function still has time to call .then_() before it finishes and the associated .then_()ed functions are attached correctly.
--
-- WARNING: If you call .then_() AFTER calling .run(), your .then_()ed function may not be called correctly!
-- @returns Promise The current promise, for daisy chaining purposes.
function Promise.run(self)
-- TODO update self.state here
do_run(self, {}, 1, function(...)
end)
return self
end
return Promise
-- return Promise
-- TEST example code, TODO test this
-- function test()
-- return Promise.new(function(resolve, reject)
-- -- TODO do something asyncy here
-- print("DEBUG running test function")
-- resolve(4)
-- end)
-- end
function test()
return Promise.new(function(resolve, reject)
-- TODO do something asyncy here
print("DEBUG running test function")
resolve(4)
end)
end
-- test()
-- .then_(function(val)
-- print("DEBUG then #1 VAL", val)
-- return val * 2
-- end)
-- .then_(function(val)
-- print("DEBUG then #2 VAL", val)
-- return math.sqrt(val)
-- end)
-- .then_(function(val)
-- print("DEBUG then #3 VAL", val)
-- end)
-- .run()
test()
:then_(function(val)
print("DEBUG then #1 VAL", val)
return val * 2
end)
:then_(function(val)
print("DEBUG then #2 VAL", val)
return math.sqrt(val)
end)
:then_(function(val)
print("DEBUG then #3 VAL", val)
end)
:run()