Timers are used for generating PWM, timing events (using input capture) and to provide the millis() timing functionality. A timer has a clock source - usually the system clock, prescaled by some factor of two, and a "count" register which counts up or down (sometimes direction is configurable, sometimes not); the size of this count register (in bits) determines the "period" of the timer (how many clocks it can count before overflowing), and the resolution of PWM it can generate. Timers also have additional functionality on top of that; - most can do "waveform generation", otherwise known as PWM, all can generate interrupts upon overflow (and sometimes in other events), and some (usually 16-bit ones) can do "input capture" where upon a pre-selected pin changing state or event, the value of the timer will be copied to a "capture" register and an inrterrupt triggered - this allows precise timing of events (useful for timing events) without requiring the full attention of the processor (as is the case with something like pulseIn).
Most timers are synchronous - that is, they are always clocked from the (optionally prescaled) system clock, and there is no delay between when changes to configuration registers are made, and when they take effect. Some timers however are not constrained in this way: they can run at a different frequnecy provided by an external clock source, or may run at some multiple of the system clock (via a PLL), allowing higher frequency PWM or timing against an external timebase. Depending on the specifics, this can provide useful functionality, but it also makes them more difficult to work with because it introduces a "synchronization delay" whcen changing the values ocertain registers. Most often in the land of Arduino, these features aren't needed, and the async timers are just like normal ones - but more of a pain to work with.
One example of this: the Optiboot bootloader sets up a timer, sets it's count register to (TOP minus a certain number of ticks), sets it running, and polls the TOV1 bit in a busy-wait loop. This is used to provide the timing for the "triple blink" while Optiboot is running; the number of ticks (and the prescaler) is chosen so it will take about 1/8th of a second to overflow. On the ATtiny861, however, this didn't work - half of the delays were only a few clock cycles long. Eventually it became evident what was happening here - the timer used is asyncronous (the x61 - like the x5 - has only the 8-bit sync. timer0 and the async "high speed" timer1). The clearing of TOV1 flag sometimes had not completed by the time it reached the while loop. It would then check for the TOV1 flag, which was still there, and then immediately exit that while loop! Solving this required simply waiting for the bit to be cleared before checking for being set again, but realizing that was what was happening took at least four debugging sessions (with the first three ending in defeat). Another example is TCD0 on the tinyAVR 1-series to generate PWM for megaTinyCore; in that case, due to the synchronization issue, the output compare registers aren't automatically updated when you change them (part of this behavior may be due to a silicon bug). It took about a year before i got millis() working with TCD9 for it's timekeeping - and it was another year after that before I got PWM and millis() working at the same time...
While for the most familar use of PWM, dimming an LED, all that matters is the frequency (And even then, we only care that it's fast enough that the human eye can't see the flicker), for more complicated applications, particularly motor control .
The timer counts in one direction until it reaches the maximum, then wraps back to other end. Assuming it's counting up, the output pin is set at the start of the cycle and cleared when it matches the "compare value" (which sets the duty cycle). If downcounting, the reverse is true.
The timer first counts in up, and then counts back down (in each direction, it works as described above. Hence, the frequency is half what it would be if the timer were using Fast PWM. Timers offering this option will generally apply updates to the compare and TOP values at the TOP of the cycle preventing a "glitch" when the compare value is changed in the middle of a cycle. This makes them better suited to motor control applcations. The fact that the frequwncy is half what it otherwise would be for the same resolution can be useful for getting the desired frequency without sacrificing resolution.
Almost every classic AVR has the same Timer0 - 8 bits, no frills timer with two PWM channels. This is almost invariably used for millis() on Arduino, as it is both the least interesting timer to take over for other uses, and is present everywhere, simplifying the implementation of millis across different devices. In that configuration they are used in fast PWM mode. They do not support input capture, amd support Fast PWM and Phase Correct PWM modes. They can be clocked from system clock prescaled by 1, 8, 64, 256, or 1024, or from an external clock on a specified pin - hwoever this external clock is sampled once per system clock, so the frequency should be less than half the system clock. Changing TOP is only possible by sacrificing one of the compare channels.
Most (but not all) classoc AVR parts have at least one of these timers. Sometimes known as "the good timer" as it is by far the most useful for typical applications. It supports full 16-bit PWM in fast, phase correct, and phase and frequency correct mode (though for Arduino purposes, it is set up in 8-bit mode, to work with the 8-bit value passed to analogWrite. These support input capture on a specific pin, and can often be configured to instead be controlled by the analog comparator (note that a reference voltage can be set as the other side of the analog comparator, this "trick" allows the analog comparator input pin to be used instead of the single ICR pin). Note that becasue the timer is not automatically reset when doing input capture, your code has to track the successive capture values, or else reset it to 0 every time a capture interrupt occurs; if you care about both HIGH and LOW times, you also need to switch the edge it's looking for in each interrupt (though, for many use cases, you do not)