In asynchronous communication you have a defined speed, the baudrate.
The receiver knows how long a bit time is. It waits for an edge and then starts counting till it is in the middle of the bit-time. Then it samples the input.
Waiting for en edge is done using 'oversampling'. You read the input status much faster then the bit rate. Common is to use 16x oversampling, but 8x also works.
There is free software that implements a UART.
If you want to see how it is done in hardware find Verilog source code. If you know C you can almost* read Verilog code.
*Apart from some very important details :-)!
Sorry missed out on the 'start and end of their bytes' part.
A UART starts with a start bit which is always low. It ends with a stop bit which is always high. Thus you wait for a 0 on the line and you know that is the start bit. You then count e.g. 10 bits (start, 8 data, stop) and the tenth bit should be high. It is very well possible that a continuous bit stream is sampled at the wrong point and still honors the 'start is low stop is high' protocol. I therefore try to have gasp between bytes to prevent this.