If I run a function in loop() or setup(), and the function doesn't work after some time, is there a way to escape the function and return to the code?
In general, the right thing to do in such a situation is to fix the
initialize() function so that it always returns. If there is a timeout
to be handled, it should be handled within that function. This is likely
to be the easiest and cleanest solution.

However, if for whatever reason that is not feasible (maybe it comes
from a library that is too opaque to edit), then the idea suggested by
Michel Keijzers is a viable alternative:


  [...] set up a timer interrupt and start it just before you initialize
  the device/component you want to initialize. When the timer interrupt
  triggers and the device is not initialized, behave the same like a
  timeout has occurred.


The issue with this is that you would want the ISR (interrupt service
routine) to return to the point where initialize() was called. This is
not trivial, but it can be done. Actually, and contrary to what my own
comment says, it can be handled without resorting to assembly hacks, by
using the setjmp “non-local goto” feature of the avr-libc. Here is a
proof of concept:

#include &lt;setjmp.h&gt;

jmp_buf env;

// Simulate an initialization that can hang. Connect pin 8 to ground if
// you want the function to ever return.
void initialize()
{
    pinMode(8, INPUT_PULLUP);
    while (digitalRead(8) == HIGH)
        ;  // wait
}

// This fires when the initialization times out.
ISR(TIMER1_OVF_vect)
{
    TIMSK1 = 0;       // disable timer interrupt
    longjmp(env, 1);  // go to where setjmp() was called
}

void setup()
{
    Serial.begin(9600);

    // Configure Timer 1 to fire an interrupt in about 4 seconds.
    TCCR1A = 0;           // reset timer
    TCCR1B = 0;
    TCNT1  = 0;
    TIFR1  = _BV(TOV1);   // clear interrupt flag
    TIMSK1 = _BV(TOIE1);  // enable timer overflow interrupt
    TCCR1B = _BV(CS10)    // clock at F_CPU / 1024
           | _BV(CS12);   // ditto

    // Attempt initialization.
    if (setjmp(env) == 0) {

        // This is the normal program flow.
        Serial.println("Attempting initialization.");
        initialize();
        TIMSK1 = 0;  // disable timer interrupt
        Serial.println("Initialization succeeded.");
    } else {

        // We get here through longjmp() in case of timeout.
        Serial.println("Initialization aborted on timeout.");
    }

    Serial.println("Moving on to the rest of the program.");
}

void loop(){}


If pin 8 is grounded, the initialization succeeds and the program prints



Attempting initialization.
Initialization succeeded.
Moving on to the rest of the program.


If pin 8 is left floating, the initialization hangs and the program
prints

Attempting initialization.
Initialization aborted on timeout.
Moving on to the rest of the program.


with a 4 second pause between the first and second lines.