In an asynchronous FIFO, one clock domain is associated with the write port, and the "head" pointer (the next write address) is kept in that clock domain. Similarly, the other clock domain is associated with the read port, and the "tail" pointer is kept there.
The problem is that both clock domains need to be able to keep track of the number of words in the FIFO, and the way to do this is to subtract the value of the tail pointer from the value of the head pointer, modulo the size of the RAM. Therefore, each pointer is encoded as Gray code, transferred to the other clock domain and converted back to binary.
It doesn't matter if more than one write happens during a read clock period, or vice-versa. The point is, with Gray code encoding, only one bit changes between any pair of values. If one clock happens to catch a transition in the other clock domain, at most only one bit can be metastable, and the ambiguity is between two adjacent states of the counter.
In other words, each side sees a monotonically increasing sequence of values from the other side, even if some values are skipped — and more importantly, even if metastability occurs. Therefore, it isn't possible for either clock domain to calculate an erroneous value for the number of words in the FIFO — it simply becomes a question of whether it gets the update one clock sooner or later than it otherwise might have.
So, on the write side, the head pointer increments directly, increasing the depth of the FIFO, but the updated tail pointer coming from the other side may be delayed. This can only cause the write side to overestimate the number of words in the FIFO, and therefore, the FIFO will never overflow.
Similarly, on the read side, the tail pointer increments directly, decreasing the depth of the FIFO, but the updated head pointer may be delayed. This can only cause the read side to underestimate the number of words in the FIFO, and as a result, it will never underflow.
In fact, you can put any number of synchronizing stages in the path of the Gray code transfers, and the only effect this will have is to increase the latency through the FIFO. This is even a configurable parameter in the Xilinx dual-clock FIFO generator.