Each time loop() ends the Arduino runs loop(). That is like a wheel that turns, moving forward along your real-time tasks.
Arduino operates faster than thought. Even analog reads, a very slow operation compared to almost all the rest is still done at about 9000 times per second. Digital reads take less than a micosecond.
Your door moves much slower than that so you code to start the movement and at least wait for the door to close before you end the move. Loop() could run many millions of times between start and end even if it takes less than 1/2 second for the door to close. So *start* is one operation you do at one time and *stop* is another to do at a later time. But both fit inside of loop(), or you call functions to do those inside loop().
You might even add some kind of switches or sensors to tell you when the door has reached the desired positions and have the controller watch for the door to finish the move commanded and perhaps keep track of how long that is taking in case the door jams unexpectedly. You would if you moved the door by hand wouldn't you?
But hey, it's your motor, your door and your chickens.
You should understand that time is involved in the task you want done every bit of the way and code for it. Whether it's 3 hours or 3 milliseconds, handle the start and end of the interval as separate events because to Arduino those are like 3 months or 3 minutes where from one line of code to the next is less than 1 second.
The controller will do as instructed. It is up to you to to say what to do and when. Using millis() makes it easier to handle the when(s) and using a state machine and/or sensor checks (if ready then do, if done then stop, if timeout then stop and tell someone, etc) makes it easier to set up sub-tasks inside loop() that do not have to fit inside the logic/braces of each other.
pseudocode -- just to show an idea of how it's done:
if time to open the door -- command to start open the door -- end if
if command to start open the door -- start the motor to open, command to watch the door open -- end if
if command to watch the door open -- if the door is open the stop the door, command nothing to happen -- end if
// etc for time to close and what else you might want done
None of those subtasks is inside the logic of another. That means less logic tangles to puzzle out and debug. Every task requires a certain amount of complexity, this helps reduce the excess.
The subtasks run on either time or what command was given previously, or both. The 'command nothing' is just to clear the last command.
New tasks can be added that depend on the state of sensor(s) or serial.available or what you want without having to fit those inside the others. Keep those simple and short, don't try to do everything in 1 pass through loop() and your code will ultra-responsive without extra complication in how the pieces fit.
We sometimes call the command the 'state of operations' or some other phrase with the word 'state' in it. The code is referred to as a state machine. But it's really as simple as keeping track of what is being done to do what is needed each time through loop().
You can have more than one state/command to be able to do more than 1 thing at a time.
State/command is just a value you keep in a global or static variable so that each time through loop() your code "knows what it was doing then to know what to do now".
I hope this helps you understand.