diff --git a/monarch/monarch.lua b/monarch/monarch.lua index 360f773..fb82671 100644 --- a/monarch/monarch.lua +++ b/monarch/monarch.lua @@ -1,4 +1,5 @@ local callback_tracker = require "monarch.utils.callback_tracker" +local async = require "monarch.utils.async" local M = {} @@ -744,8 +745,9 @@ function M.show(id, options, data, cb) pop = pop - 1 end stack[#stack] = nil - show_out(top, screen, WAIT_FOR_TRANSITION, callbacks.track()) - callbacks.yield_until_done() + async(function(await, resume) + await(show_out, top, screen, WAIT_FOR_TRANSITION, resume) + end) top = stack[#stack] end @@ -784,8 +786,9 @@ function M.show(id, options, data, cb) local same_screen = top and top.id == screen.id if same_screen or (options and options.sequential) then if top then - show_out(top, screen, WAIT_FOR_TRANSITION, callbacks.track()) - callbacks.yield_until_done() + async(function(await, resume) + await(show_out, top, screen, WAIT_FOR_TRANSITION, resume) + end) end show_in(screen, top, options and options.reload, add_to_stack, WAIT_FOR_TRANSITION, callbacks.track()) else @@ -872,16 +875,11 @@ function M.clear(cb) log("clear() queuing action") queue_action(function(action_done, action_error) - local co - co = coroutine.create(function() - - local callbacks = callback_tracker() - + async(function(await, resume) local top = stack[#stack] while top and top.visible do stack[#stack] = nil - back_out(top, screen, WAIT_FOR_TRANSITION, callbacks.track()) - callbacks.yield_until_done() + await(back_out, top, screen, WAIT_FOR_TRANSITION, resume) top = stack[#stack] end @@ -889,12 +887,9 @@ function M.clear(cb) table.remove(stack) end - callbacks.when_done(function() - pcallfn(cb) - pcallfn(action_done) - end) + pcallfn(cb) + pcallfn(action_done) end) - assert(coroutine.resume(co)) end) end @@ -907,6 +902,7 @@ function M.back(data, cb) queue_action(function(action_done) local callbacks = callback_tracker() + local back_cb = callbacks.track() local screen = table.remove(stack) if screen then log("back()", screen.id) @@ -918,7 +914,7 @@ function M.back(data, cb) if data then top.data = data end - back_in(top, screen, WAIT_FOR_TRANSITION, callbacks.track()) + back_in(top, screen, WAIT_FOR_TRANSITION, back_cb) end) else if top then @@ -926,10 +922,10 @@ function M.back(data, cb) top.data = data end back_in(top, screen, DO_NOT_WAIT_FOR_TRANSITION, function() - back_out(screen, top, WAIT_FOR_TRANSITION, callbacks.track()) + back_out(screen, top, WAIT_FOR_TRANSITION, back_cb) end) else - back_out(screen, top, WAIT_FOR_TRANSITION, callbacks.track()) + back_out(screen, top, WAIT_FOR_TRANSITION, back_cb) end end end diff --git a/monarch/utils/async.lua b/monarch/utils/async.lua new file mode 100644 index 0000000..92a1f5a --- /dev/null +++ b/monarch/utils/async.lua @@ -0,0 +1,47 @@ +local M = {} + +local NOT_STARTED = "not_started" +local YIELDED = "yielded" +local RESUMED = "resumed" +local RUNNING = "running" + +function M.async(fn) + local co = coroutine.running() + + local state = NOT_STARTED + + local function await(fn, ...) + state = RUNNING + fn(...) + if state ~= RUNNING then + return + end + state = YIELDED + local r = { coroutine.yield() } + return unpack(r) + end + + local function resume(...) + if state ~= RUNNING then + state = RUNNING + local ok, err = coroutine.resume(co, ...) + if not ok then + print(err) + print(debug.traceback()) + end + end + end + + if co then + return fn(await, resume) + else + co = coroutine.create(fn) + return resume(await, resume) + end +end + +return setmetatable(M, { + __call = function(t, ...) + return M.async(...) + end +}) \ No newline at end of file diff --git a/monarch/utils/callback_tracker.lua b/monarch/utils/callback_tracker.lua index 8593263..adde1e6 100644 --- a/monarch/utils/callback_tracker.lua +++ b/monarch/utils/callback_tracker.lua @@ -6,13 +6,18 @@ function M.create() local callback = nil local callback_count = 0 + local all_callbacks_done = false local function is_done() return callback_count == 0 end local function invoke_if_done() + if all_callbacks_done then + print("Warning: The same callback will be invoked twice from the callback tracker!", id or "") + end if callback_count == 0 and callback then + all_callbacks_done = true local ok, err = pcall(callback) if not ok then print(err) end end @@ -41,20 +46,6 @@ function M.create() invoke_if_done() end - function instance.yield_until_done() - local co = coroutine.running() - callback = function() - local ok, err = coroutine.resume(co) - if not ok then - print(err) - end - end - invoke_if_done() - if not is_done() then - coroutine.yield() - end - end - return instance end