Jump to content

What happens when return is executed in this code?

shivajikobardan
Go to solution Solved by mariushm,

Let's  go through it step by step. Probably gonna get something wrong but that's how I imagine it.

 

Everything starts with

window.requestAnimationFrame(main);

In browser's memory there's a LIST of  function names, of what functions - if any - to call when browser is ready to do that frame.  The function above just puts an entry in the list saying "browser, when you're about to do that frame, call the function named main".  The function exists as soon as "main" is added to that LIST.

Your main function DOES NOT run at this point.

 

At some point in the future, could be right away, could be milliseconds, could be seconds later, when browser is about to do that frame, it looks up in the internal list to see if there's some callback function there and finds the "main" function there. So, the browser deletes the only entry from the internal LIST making the list empty and then launches main function and waits until the main function finishes to start rendering/drawing/whatever the frame.

Now you're inside the main function.

In the main function, you do .requestAnimationFrame again adding yourself (the main function) as callback function. The requestAnimationFrame looks up at that internal LIST and adds the main function as callback and exists. Now, control comes back to the main function which checks if enough time has passed since it last ran, and if not enough time has passed the main function just exists.  If enough time has passed, the function updates the last time variable and runs the game logic by calling GameEngine();

After GameEngine() runs, the main function exists and the browser finally "paints" / renders/ draws  the frame

 

After the browser finishes updating the frame, and maybe a few ms more (for example browser may do 60 fps which means one frame every 1000/60 = 16ms but finishes rendering the frame in ms, in this case browser may waits 15 ms doing nothing)  it's time for browser to render another frame, so it looks again in that internal LIST and spots the entry which says it should call "main" function again, so it removes the entry from the list (making the list empty) and calls main function.

 

If the main function doesn't add itself to that internal LIST every time by using the requestAnimationFrame function, the internal LIST will be emptied and the main function will no longer be called.

 

I think what you don't understand is that requestAnimationFrame is sort of async, it just puts the function name in an internal list and exists, it doesn't launch the main function right there, the main function is queued to run at some point in the future when the browser is about to draw another frame.

 

 

 

I'm currently watching tutorials to build projects as I'm still not in a phase where I can carve a project that I want all on my own.
Currently, working on a snake game.

let speed = 2;
let lastPaintTime = 0;

//Game functions
function main(ctime) {
  window.requestAnimationFrame(main);
  if ((ctime - lastPaintTime) / 1000 < 1 / speed) {
    return;
  }
  lastPaintTime = ctime;
  gameEngine();
}

//Main logic starts here
window.requestAnimationFrame(main);

My confusion:
What happens when return is executed in this code?  According to chatGPT, the function terminates. I get that. But does that mean it won't be called again via "Main logic starts here" part?


 

Link to comment
Share on other sites

Link to post
Share on other sites

seems like that .requestAnimationFrame - see https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame  -  is launched with the main function as callback , so each time the browser triggers a frame redraw  that main function will be called.

if ((ctime - lastPaintTime) / 1000 < 1 / speed) {
    return;
  }

Depending on the browser performance you could have that main function triggered anything from less than once a second to at least 60 times a second so inside the main function  there's that check:

 

So the function compares the current time, to previous  time when calculations were done and if the elapsed time is less than 1/speed (in your case 1/2 or half a second) the function ends, and the game logic doesn't happen.

Only if more than 1/speed time has passed from previous game calculations, then lastPaintTime is updated and gameEngine is called to run the game logic (update scores, check if snake ate something, put another something on the game area, whatever)

lastPaintTime = ctime;
  gameEngine();
Link to comment
Share on other sites

Link to post
Share on other sites

41 minutes ago, shivajikobardan said:

hmm my question is different I think.. Thank you anyways.

Have you clicked on the link @mariushm sent you?

 

"Note: Your callback routine must itself call requestAnimationFrame() again if you want to animate another frame at the next repaint. requestAnimationFrame() is 1 shot."

ಠ_ಠ

Link to comment
Share on other sites

Link to post
Share on other sites

12 minutes ago, shadow_ray said:

Have you clicked on the link @mariushm sent you?

 

"Note: Your callback routine must itself call requestAnimationFrame() again if you want to animate another frame at the next repaint. requestAnimationFrame() is 1 shot."

yes

Link to comment
Share on other sites

Link to post
Share on other sites

1) window.requestAnimationFrame(main) is called.
2) Again inside main, window.requestAnimationFrame(main) is called.

So, the condition is never checked?

Link to comment
Share on other sites

Link to post
Share on other sites

3 minutes ago, shivajikobardan said:

1) window.requestAnimationFrame(main) is called.
2) Again inside main, window.requestAnimationFrame(main) is called.

So, the condition is never checked?

Quote

The callback method is passed a single argument, a DOMHighResTimeStamp, which indicates the current time (based on the number of milliseconds since time origin). When multiple callbacks queued by requestAnimationFrame() begin to fire in a single frame, each receives the same timestamp even though time has passed during the computation of every previous callback's workload (in the code example below we only animate the frame when the timestamp changes, i.e. on the first callback). This timestamp is a decimal number, in milliseconds, but with a minimal precision of 1ms (1000 µs).

 

You can do  .requestAnimationFrame(function1) followed by .requestAnimationFrame(function2)  and function1 and function2 will be called one after the other.

Your main function is called before the browser is gonna render, and when you're inside main and use the requestAnimation function , you're placing main function in  QUE again... the request function will accept the main and place it in que and exit and you're gonna be back in your code where you do the time lapse checks and game logic

 

 

Link to comment
Share on other sites

Link to post
Share on other sites

My confusion has now been added up.

 

My confusion:
"If condition" should never be checked on this code. Because:
1) window.requestAnimationFrame(main): It calls main function.
2) At the very first line of main function, it again calls main function. So the control should go to main function and forever it should keep calling itself.
3) The if condition should never be checked.
 

Link to comment
Share on other sites

Link to post
Share on other sites

Let's  go through it step by step. Probably gonna get something wrong but that's how I imagine it.

 

Everything starts with

window.requestAnimationFrame(main);

In browser's memory there's a LIST of  function names, of what functions - if any - to call when browser is ready to do that frame.  The function above just puts an entry in the list saying "browser, when you're about to do that frame, call the function named main".  The function exists as soon as "main" is added to that LIST.

Your main function DOES NOT run at this point.

 

At some point in the future, could be right away, could be milliseconds, could be seconds later, when browser is about to do that frame, it looks up in the internal list to see if there's some callback function there and finds the "main" function there. So, the browser deletes the only entry from the internal LIST making the list empty and then launches main function and waits until the main function finishes to start rendering/drawing/whatever the frame.

Now you're inside the main function.

In the main function, you do .requestAnimationFrame again adding yourself (the main function) as callback function. The requestAnimationFrame looks up at that internal LIST and adds the main function as callback and exists. Now, control comes back to the main function which checks if enough time has passed since it last ran, and if not enough time has passed the main function just exists.  If enough time has passed, the function updates the last time variable and runs the game logic by calling GameEngine();

After GameEngine() runs, the main function exists and the browser finally "paints" / renders/ draws  the frame

 

After the browser finishes updating the frame, and maybe a few ms more (for example browser may do 60 fps which means one frame every 1000/60 = 16ms but finishes rendering the frame in ms, in this case browser may waits 15 ms doing nothing)  it's time for browser to render another frame, so it looks again in that internal LIST and spots the entry which says it should call "main" function again, so it removes the entry from the list (making the list empty) and calls main function.

 

If the main function doesn't add itself to that internal LIST every time by using the requestAnimationFrame function, the internal LIST will be emptied and the main function will no longer be called.

 

I think what you don't understand is that requestAnimationFrame is sort of async, it just puts the function name in an internal list and exists, it doesn't launch the main function right there, the main function is queued to run at some point in the future when the browser is about to draw another frame.

 

 

 

Link to comment
Share on other sites

Link to post
Share on other sites

 

This figure from this blog helped me clear my concepts related to this.

Source: https://nainacodes.com/blog/understand-the-event-loop-in-javascript

 

1) Initially, window.rAF is passed to call stack. 

 

2) By definition, rAF waits till next repaint of window which is 16ms for 60fps screen. It's like setTimeOut but better.

 

JKFMe_1CrG00ZF1aQvwSRXzkvvQt74HWMJ2v5zXEwL-WcDL2t7EqlmvT40JUD8fzwZMKxExa7eEB9uIOKkCkxjCcsHLuuxPETNpCODJNE41vaSYe0_T4OvB5aRL-4N9oupU3GJ3yAIKyWIkybL7pSaF8-VMxZFyTcVINEVMnayM8qgAJ7jcxK_38gjKOWw

3) window.rAF() is executed, so it is removed from the call stack. Call stack is empty. Meanwhile at the same time, after 16ms has been passed, main() has been put into callback queue.

 

iMJXIn_Ki6FeZXcC6Bph2WZ3zikSprzH8cXQKw4pid9mHLyEpuPad8y0xsNcGTzE35y_AjM7AArG4oJnB-ut5d6ZDw410UGmPi6y6ka78NRYxB3fr2yQQpQGXowCecttC-FLAyhsaFHiR59MzPzCxQVbhNgtxDUGD7OpTrZKZg8KdlSMEbszlgPsiUV-9w

 

Now here is where the event loop comes in. The event loop is a continuous running process that constantly checks if the call stack is empty or not. If the call stack is empty, it will move the function from the callback queue into the call stack and it gets executed. So, main() gets executed. 

3uC2V6xMGTbu3xIF0ud_5M4frXUI8Qtn6PuBV9_oVj4MPKXix5VupB2ZYv_eonZG0Dhj4-a_ZQtIDVAmYlXUPAYH2gQTuq_Ha9L4JRtadPqzUVjtgRmI3qNKEGQvLxujnnU6iFwa9cY-PrH189cYU5OI7RpgbBKM0qZx02VhFmue89SDwQ-A64nUCHoYjQ

4) Again, it calls window.rAF(main).

JKFMe_1CrG00ZF1aQvwSRXzkvvQt74HWMJ2v5zXEwL-WcDL2t7EqlmvT40JUD8fzwZMKxExa7eEB9uIOKkCkxjCcsHLuuxPETNpCODJNE41vaSYe0_T4OvB5aRL-4N9oupU3GJ3yAIKyWIkybL7pSaF8-VMxZFyTcVINEVMnayM8qgAJ7jcxK_38gjKOWw

window.rAF() has now executed.

 

5) Now, the code under window.rAF() runs(the if condition and so on and so forth). After, 16ms, main() is passed to callback queue.

iMJXIn_Ki6FeZXcC6Bph2WZ3zikSprzH8cXQKw4pid9mHLyEpuPad8y0xsNcGTzE35y_AjM7AArG4oJnB-ut5d6ZDw410UGmPi6y6ka78NRYxB3fr2yQQpQGXowCecttC-FLAyhsaFHiR59MzPzCxQVbhNgtxDUGD7OpTrZKZg8KdlSMEbszlgPsiUV-9w

 

main() gets executed.

3H69trSEAVPpgwdhv5jiCT7d4ZxpuIm3c_QRaiMctURLcHIIatfU8OBzzWjm7uZVjyOUDPreLk9GhdTCfWju0pXakByuALkimellFP_TeHtm14L8LnO5i5k97nN1oNqFVYHygize4WoCOyLQWnyenlkQItAfndMj8A92GhqmhDcmaN_mSD22Gr6lnKALeA






 

Link to comment
Share on other sites

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×