If you want to run something after the stack clears, run it on a 0 second timer.
(run-with-timer 0 nil 'your-function)
You could wrap this is a macro like this:
(defmacro after-stack-clears (&rest body)
"Do BODY after the call stack is clear"
`(run-with-timer 0 nil (lambda () ,@body)))
Here's an example usage mimicking your described scenario.
In this example,
annoying-func is calling another provided function. In the provided function you'd like to call
annoying-func again but
annoying-func will error if called more than once on the call stack. So to counter this, the provided macro to ensure that
annoying-func is called a second time, but only after the call stack has cleared and
annoying-func has done its cleanup
(defun your-func-simple ()
"try to directly call `annoying-func'."
(annoying-func (lambda () (message "hi"))))
(defun your-func-smart ()
"ensure `annoying-func' is called after the stack clears."
(annoying-func (lambda () (message "hi")))))
(setq annoying-var nil)
(defun annoying-func (func)
"This function will apply FUNC, if `annoying-function' is called
somewhere down the line by FUNC, the second call will error, because this
is the annoying function after all."
(error "Can't call recursively!")
(progn (setq annoying-var t)
(apply func nil)
(setq annoying-var nil))
(error (progn (message "errored! %s" err) (setq annoying-var nil))))))
(defun erroring-example ()
(defun working-example ()
;; will error due to annoying-func being called twice on the stack
;; will NOT error since annoying-func is called again only after the stack has cleared