Most preemptive schedulers will, highly simplified, allocate a certain maximum time to each process.
When that time expires (for instance 10 ms), it will re-schedule so that other processes get some CPU.
If the timer doesn't expire before the process hits some other wait condition (such as doing I/O), it will re-schedule then, instead.