I think to do it as gracefully as you describe, where you could then provide feedback to the user, you’d need to tie the function you are calling to a thread, see here.
This example here may help you develop the stop button part. This timer code in a working notebook here that I found in my search attempt on Github illustrates some of the concepts well, too, and may help you integrate what you require. (The first example also works where I suggested running the timer notebook in the gist.)