In the case where ping is outputting to a shell pipeline, the different behavior is explained by buffering. ping calls fflush() after reporting each successfully received echo packet, so those lines are sent immediately to cat, which outputs them without delay. ping does not call fflush() after writing out the lines that report timed out packets, so stdio buffers those lines until ping calls exit(), which then sends them all to cat, which outputs them.
In the case where ping is outputting to the terminal directly, you see both the success and failure output immediately because by default stdio line buffers all output to streams associated with ttys. So each output line is sent to the terminal immediately instead of being buffered by stdio.
In the spirit of teaching a man to fish, you can verify all this yourself by reading the OS X source code for ping and reading the OS X man page for stdio.