mirror of
https://github.com/sbrl/Minetest-WorldEditAdditions.git
synced 2024-11-01 05:43:01 +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
|
--- Javascript Promises, implemented in Lua
|
||||||
-- In other words, a wrapper to manage asynchronous operations.
|
-- 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.
|
-- 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)
|
function Promise.new(fn)
|
||||||
local result = {
|
local result = {
|
||||||
state = "pending",
|
state = "pending",
|
||||||
fns = { { then_ = fn } }
|
fns = { { fn_then_ = fn } }
|
||||||
}
|
}
|
||||||
setmetatable(result, Promise)
|
setmetatable(result, Promise)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
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 = {
|
local do_unpack = unpack
|
||||||
then_ = fn_then,
|
if not do_unpack then do_unpack = table.unpack end
|
||||||
}
|
if not do_unpack then
|
||||||
if type(fn_catch) == "function" then
|
error("Error: Failed to find unpack implementation")
|
||||||
step_item.catch_ = fn_catch
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(self.fns, step_item)
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
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)
|
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
|
-- 0: Termination condition
|
||||||
---
|
---
|
||||||
if #promise.fns == 0 then
|
if #promise.fns == 0 then
|
||||||
promise.state = "fulfilled"
|
promise.state = "fulfilled"
|
||||||
origin_resolve(table.unpack(args))
|
origin_resolve(do_unpack(args))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
---
|
---
|
||||||
-- 1: Sort out inputs
|
-- 1: Sort out inputs
|
||||||
---
|
---
|
||||||
if args == nil then args = {} end
|
if args == nil then args = {} end
|
||||||
local next = table.remove(promise.fns, 1)
|
local next = table.remove(promise.fns, 1)
|
||||||
|
|
||||||
local do_next_fn = function(args)
|
local do_next_fn = function(args_to_pass)
|
||||||
do_run(promise, args, depth + 1)
|
do_run(promise, args_to_pass, depth + 1, origin_resolve)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
-- 2: Run teh function!
|
-- 2: Run teh function!
|
||||||
---
|
---
|
||||||
|
|
||||||
-- (if it's the 1st one in the sequence we treat it specially)
|
-- (if it's the 1st one in the sequence we treat it specially)
|
||||||
local results
|
local results
|
||||||
if depth == 1 then
|
if depth == 1 then
|
||||||
-- This function is the inner one of a promise already.
|
-- This function is the inner one of a promise already.
|
||||||
-- It has already recieved arguments, so it expected resolve/reject instead
|
-- 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
|
if getmetatable(arg[1]) == Promise then
|
||||||
promise.state = "rejected"
|
promise.state = "rejected"
|
||||||
error("Error: Returning nested Promises is not currently supported.")
|
error("Error: Returning nested Promises is not currently supported.")
|
||||||
end
|
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
|
end, function(...) -- REJECT
|
||||||
promise.state = "rejected"
|
promise.state = "rejected"
|
||||||
if type(next.catch_) == "function" then
|
if type(next.fn_catch_) == "function" then
|
||||||
next.catch_(table.unpack(arg))
|
next.fn_catch_(do_unpack(arg))
|
||||||
else
|
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
|
-- If there's no catch registered, throw an error
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
else
|
else
|
||||||
results = { next.then_(table.unpack(args)) }
|
results = { next.fn_then_(do_unpack(args)) }
|
||||||
|
|
||||||
---
|
---
|
||||||
-- 3: Handle the aftermath
|
-- 3: Handle the aftermath
|
||||||
---
|
---
|
||||||
|
@ -121,35 +104,67 @@ local function do_run(promise, args, depth, origin_resolve)
|
||||||
do_next_fn(results)
|
do_next_fn(results)
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
|
|
||||||
return Promise
|
|
||||||
|
|
||||||
|
-- return Promise
|
||||||
|
|
||||||
|
|
||||||
-- TEST example code, TODO test this
|
-- TEST example code, TODO test this
|
||||||
|
|
||||||
-- function test()
|
function test()
|
||||||
-- return Promise.new(function(resolve, reject)
|
return Promise.new(function(resolve, reject)
|
||||||
-- -- TODO do something asyncy here
|
-- TODO do something asyncy here
|
||||||
-- print("DEBUG running test function")
|
print("DEBUG running test function")
|
||||||
-- resolve(4)
|
resolve(4)
|
||||||
-- end)
|
end)
|
||||||
-- end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- test()
|
test()
|
||||||
-- .then_(function(val)
|
:then_(function(val)
|
||||||
-- print("DEBUG then #1 VAL", val)
|
print("DEBUG then #1 VAL", val)
|
||||||
-- return val * 2
|
return val * 2
|
||||||
-- end)
|
end)
|
||||||
-- .then_(function(val)
|
:then_(function(val)
|
||||||
-- print("DEBUG then #2 VAL", val)
|
print("DEBUG then #2 VAL", val)
|
||||||
-- return math.sqrt(val)
|
return math.sqrt(val)
|
||||||
-- end)
|
end)
|
||||||
-- .then_(function(val)
|
:then_(function(val)
|
||||||
-- print("DEBUG then #3 VAL", val)
|
print("DEBUG then #3 VAL", val)
|
||||||
-- end)
|
end)
|
||||||
-- .run()
|
:run()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue