Constant ADC sampling rate on ESP8266 or ESP32
You have several options for controlling the sampling rate.

Software only

This is the simplest, and the previous answer got it almost right. Here
is my take:

const SAMPLE_INTERVAL = 500;  // 500 microseconds

void loop() {
    static unsigned long last_sample_time;
    if (micros() - last_sample_time &gt;= SAMPLE_INTERVAL) {  // note 1
         last_sample_time += SAMPLE_INTERVAL;  // note 2
         // Take your sample here.
    }
}


Note 1: On the AVR-based boards, millis() is not accurate to the
millisecond. It tends to get late, then compensate for this by jumping
by two milliseconds at once. For controlling a process that should
happen every 1&nbsp;ms exactly, millis() is very clearly not
suitable. I do not know how good is the ESP8266's millis(). It may
well be better than the AVR version. However, even a perfect millis()
will not be good for anything faster than 1&nbsp;kHz.

The solution to this problem is pretty trivial: just count the time in
microseconds instead of milliseconds. The only drawback of using
micros() is that you cannot measure durations longer than about
71.6&nbsp;minutes, which in turn means you cannot use it for sampling at
any rate slower than 233&nbsp;µHz. Obviously this is not something you
should be concerned about. Note that the micros() rollover is not an
issue as long as you are sampling faster than the rollover period.

Note 2: It is impossible to have a steady sampling rate if it is
controlled by software. The best you can hope for is a tight control on
the average sampling period. This is accomplished by updating
last_sample_time as

last_sample_time += SAMPLE_INTERVAL;


Note that this variable holds the time when the last sample was supposed
to be taken, not the time it was actually taken. The sample was probably
taken a little bit behind schedule, as anything the CPU does takes time.
Updating last_sample_time this way means that these timing errors are
not cumulative. They would be cumulative if you did it in the “Blink
without delay” style, namely

previousMillis = currentMillis;


The drawback of this software method is that it inherently has a
significant amount of jitter. The program tests the current time only
once per loop iteration, and it may have other things to do within that
loop that adds to its execution time.

Software triggered by interrupts

I have never programmer an ESP timer but, if you can, it should provide
you better timings than the software-only version. Have the timer
deliver an interrupt at the desired sampling period, and use the ISR for
triggering the ADC. You may wait for the result inside the ISR, store it
in RAM, and set a flag to tell the main loop that a new sample is
available, as in

volatile int analog_sample;
volatile bool analog_sample_valid;

ISR(whatever_timer_interrupt)
{
    analog_sample = analogRead(ANALOG_PIN);
    analog_sample_valid = true;
}

void loop()
{
    if (analog_sample_valid) {
        int analog_sample_copy = analog_sample;  // note 1
        analog_sample_valid = false;
        // handle analog_sample_copy here
    }
}


Note 1: On an 8&nbsp;bit Arduino, you would have to do this with
interrupts disabled. You shouldn't need to disable interrupts on a
32&nbsp;bit platform.

This method should significantly reduce the jitter compared to the
software-only version. Now the sampling doesn't have to wait for the
program to run through the loop and reach the point when the time is
tested. Instead, whatever the program is doing, it will be immediately
interrupted for doing the sampling. Well... almost. From time to time it
will happen that the interrupt fires when the program is running with
interrupts disabled. This may be the case if it is already servicing
another interrupt. When this happens, the sampling will have to wait
until interrupts are enabled again. You end up with a jitter that is
most of the time a small fraction of a microsecond, and then
occasionally a sample is late by several microseconds.

Hardware only

On an AVR, you can configure the ADC to be automatically triggered by a
timer, or even to automatically trigger itself in the so called “free
running mode”. I do not know the specifics of the ADC in the ESP8266.
You may want to read the datasheet and see if it offer similar
functionality. If you can enable ADC auto-triggering, then you will have
cycle-accurate timings. The steadiness of your sampling will be just
as good as the quartz crystal clocking your MCU. No software solution
can ever come close to this level of perfection. The only drawback of
this method is that you will have to study the datasheet of your ESP in
order to implement it.