Correct way of having loop end when required?
As pointed out by Ignacio Vazquez-Abrams and jsotola, you need to use a
state machine, like the one from the Blink Without Delay
Arduino tutorial. Yours would be slightly more complicated because you
need a way to turn it on and off, and because you use different on and
off periods. Here is an example code that abstract the blinking logic
into three functions:


update_led() takes care of the blinking, and it has to be called
quite often in order to achieve a smooth blinking
start_blinking() and stop_blinking(), as their names suggest, are
used to turn the blinking state machine on and off.




const uint32_t ON_TIME  = 3000;
const uint32_t OFF_TIME = 5000;

bool led_blinking;     // are we blinking the LED?
bool led_on;           // is the LED currently on?
uint32_t last_toggle;  // last time it toggled while blinking

void update_led() {
    if (!led_blinking) return;
    uint32_t now = millis();
    if (led_on &amp;&amp; now - last_toggle &gt;= ON_TIME) {
        digitalWrite(ledPin, LOW);
        led_on = false;
        last_toggle = now;
    }
    if (!led_on &amp;&amp; now - last_toggle &gt;= OFF_TIME) {
        digitalWrite(ledPin, HIGH);
        led_on = true;
        last_toggle = now;
    }
}

void start_blinking() {
    digitalWrite(ledPin, HIGH);
    led_blinking = true;
    led_on = true;
    last_toggle = millis();
}

void stop_blinking() {
    digitalWrite(ledPin, LOW);
    led_blinking = false;
    led_on = false;
}


You then have to call these functions from loop() as follows:

void loop() {
    // Keep the LED blinking if necessary.
    update_led();
    ...
    if (request.indexOf("/LED=ON") != -1)  {
        start_blinking();
    }
    if (request.indexOf("/LED=OFF") != -1)  {
        stop_blinking();
    }
    ...
}




Edit, in response to jsotola’s comments:

When answering the question, I was initially tempted to make a
BlinkingLed class, with the functions as its public interface and the
variables private. I resisted the temptation on the grounds that, given
the apparent OP’s level of C++ knowledge, that could end up being well
over his head. Instead, I used documentation as an informal way of
separating the public from the private: you are only supposed to use
what is documented, i.e. the functions. You don't “take a shortcut in
your program and clear led_blinking directly”.

You could, of course, make the led_blinking variable part of the
public interface. That could even be a nice simplification, as you don't
need the start/stop function anymore. But then you loose control over
the initial phase of the blinking pattern. That may not be an issue, but
if you want the LED to turn on as soon as you set led_blinking to
true, then you have to do something like

void update_led() {
    uint32_t now = millis();
    if (!led_blinking) {
        digitalWrite(ledPin, LOW);
        led_on = false;
        last_toggle = now - OFF_TIME;  // fix initial phase
        return;
    }
    ...
}


Note that the subtraction can overflow, but that's not as issue because
it follows the rules of modular arithmetics.

I actually kind of like this option, with all the logic in a single
function. This way the variables led_on and last_toggle can be made
static local to the function, which provides true encapsulation without
a class. However, from a pedagogic point of view, I think my initial
proposal is easier to understand.