write_location and flush_location of pg_stat_replication view
The two columns in pg_stat_replication are basically pointing out the difference between write(2) and fsync(2)

From the write(2) manpage:


  write() writes up to count bytes from the buffer pointed buf to the file referred to by the file descriptor fd.
  
  The  number  of  bytes  written  may be less than count if, for example, there is insufficient space on the underlying physical medium, or the RLIMIT_FSIZE resource
  limit is encountered (see setrlimit(2)), or the call was interrupted by a signal handler after having written less than count bytes.  (See also pipe(7).)
  
  For a seekable file (i.e., one to which lseek(2) may be applied, for example, a regular file) writing takes place at the current file offset, and the file offset is
  incremented  by  the number of bytes actually written.  If the file was open(2)ed with O_APPEND, the file offset is first set to the end of the file before writing.
  The adjustment of the file offset and the write operation are performed as an atomic step.
  
  POSIX requires that a read(2) which can be proved to occur after a write() has returned returns the new data.  Note that not all file systems are POSIX conforming.


From the fsync(2) man page:


  fsync() transfers ("flushes") all modified in-core data of (i.e., modified buffer cache pages for) the file referred to by the file descriptor fd to the disk device
  (or other permanent storage device) where that file resides.  The call blocks until the device reports that the transfer has completed.  It  also  flushes  metadata
  information associated with the file (see stat(2)).
  
  Calling  fsync()  does  not  necessarily  ensure  that the entry in the directory containing the file has also reached disk.  For that an explicit fsync() on a file
  descriptor for the directory is also needed.
  
  fdatasync() is similar to fsync(), but does not flush modified metadata unless that metadata is needed in order to allow a subsequent data retrieval to be correctly
  handled.   For  example,  changes  to  st_atime  or  st_mtime (respectively, time of last access and time of last modification; see stat(2)) do not require flushing
  because they are not necessary for a subsequent data read to be handled correctly.  On the other hand, a change to the file size (st_size, as  made  by  say  ftrun-
  cate(2)), would require a metadata flush.
  
  The aim of fdatasync() is to reduce disk activity for applications that do not require all metadata to be synchronized with the disk.


Under normal operation by default, PostgreSQL calls fsync (or fdatasync, if you're on linux), to ensure that the data in a WAL segment is well and truly written to disk. 

In normal operation, when a write call happens, it's basically copying that data from PostgreSQL to the filesystem cache, which is just another bit of RAM. If the server crashes, then you will still lose that data, because it's still in RAM, even though it's just a different part of RAM.

On COMMIT, though, PostgreSQL calls fsync, which forces the WAL segment data to be written out to disk, not just to the filesystem cache in RAM. Which ensures that after a crash, committed data is still committed, and can be replayed. 

It also has the added advantage of needing to call fsync fewer times for the data files, because all of the necessary data to properly replay the changes to those files are contained in the WAL segments, which are durably persisted to disk. 

A nice description of this behavior under Linux can be found in the article Flushing Disk Buffers. 

The syncing behavior can be controlled with wal_sync_method and fsync

Hope that helps answer your question. =)