Hitting the stop button does not immediately force the script to go to vuser_end(). The default behavior is just as you describe, each actively running vuser will complete it's current iteration, including all transaction defined there, then execute any code in vuser_end, then stop.
If you want to change this behavior there are options in the Controller if you go to:
Tools > Options > Run-Time Settings
From there you can modify the behavior of manually stopping vusers in your scenario.