The reasoning for using almost-empty and almost-full is to provide an ‘advance’ warning to the rest of the system that the limits of the FIFO are being reached. This gives the system more time to react and avoid over- or under-run conditions.
For example, a device writing a FIFO may take more than one cycle to respond to a full indication, so it could overrun. By giving an early warning with almost-full the host can see it in time, stall its writing and avoid FIFO overrun. In effect, the difference between almost-full and full forms a thing called a skid buffer. (The writer has ‘momentum’ and takes time to ‘skid’ to a stop, hence the name.)
One use of almost-empty is for the FIFO writer to know that it has to send fresh data soon before the reader fully drains the FIFO. Again, the advance warning allows the writer extra time, in this case to set up the next transfer to push some fresh data into the FIFO. This would be used for a data sink with no tolerance for stall, such as a DAC, that needs continuous data.
Another almost-empty use case is to compensate for read side response latency to the empty/not empty flag. Using almost-empty allows it more time to sample the flag and stall its reading in time to avoid reading the FIFO to underrun.
This seems to be related to the case you read about. That reader takes 1 cycle to respond to not-empty. Because of its sampling processing delay, it will read 'beyond the end' when the FIFO goes empty (that is, it will underrun) because it doesn't stop soon enough. So the dumb solution is for it to insert a ‘dead’ cycle in between each read to sample the empty flag before initiating the read.
Some protocols, like AXI-Stream, deal with this by qualifying the data with a valid flag. In this FIFO read case, not-empty is used as ‘valid’, so the reader doesn’t have to wait. Instead, it launches the read whenever it wants, but doesn’t actually forward the data unless its ‘ready’ and the FIFO ‘valid’ (not-empty) are both true.
Why would the system logic need extra latency time in the first place? It may have some architectural delay like a clock boundary cross to deal with, or there needs to be a pipeline inserted to help with timing closure.