mirror of
https://github.com/sbrl/Minetest-WorldEditAdditions.git
synced 2024-11-26 00:53:00 +00:00
promise/sbrl: bugfix
it should work in most situations now.
This commit is contained in:
parent
3ccea536c8
commit
f654bde966
1 changed files with 81 additions and 66 deletions
147
promise.lua
147
promise.lua
|
@ -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()
|
||||
|
||||
|
|
Loading…
Reference in a new issue