An evaluator is just a function that takes in some JSON and spits out a result. [if [> 2 1] 8 9] would produce 8.
Say you wanted to pause during the evaluation of the [> 2 1] condition. What do you do? How would you resume it later? Well, you have the original JSON. Meaning you have the whole program code. You know where you are in that program code. And you know the current state of the program. So you save those somewhere, return from your evaluator, then restore the values later and tell your evaluator to start from a specific location.
One non intuitive aspect of this, though, is that “the current values of your program” includes “what the evaluator should do at each step”. Imagine evaluating [if [> 2 1] 8 9]. In your eval function, you check “if the first thing in the list is “‘if’”, call the ‘eval-if’ subroutine.” Your two options here are to either (a) have eval-if evaluate the condition and then return the result, or (b) pass the condition and both branches to the function, and have that function evaluate the condition and one of the branches.
The latter form is called continuation passing, and it’s much easier to pause and resume. But you’re restricted to tail calls only. You can do it the other way by saving a stack.
I'd like to hear more about control flow state and data flow state here.
Continuations are in the category of things best explained by burrito analogies.