How can I replace pulseIn with interrupts?
You do not want the reading of the incoming pulse to block your
program execution. This is the very key requirement: you want a
non-blocking pulse reading. You can write a non-blocking replacement
for pulseIn(), but you will have to use it differently. You cannot
expect a non-blocking pulseIn() to return the pulse length whenever
you call it: instead, it would return that value only when you call it
right after the pulse falling edge. On all other circumstances it will
return a dummy value meaning “the pulse is not finished yet, thus I
cannot give you its length right now, please call me later”.

Below is an example of a function implementing this approach. You have
to call it on every loop iteration: the more often you call it, the
better resolution you will have. This function uses 0 as the dummy value
meaning “no answer available right now”:

/*
 * Non-blocking pulseIn(): returns the pulse length in microseconds
 * when the falling edge is detected. Otherwise returns 0.
 */
unsigned long read_pulse(int pin)
{
    static unsigned long rising_time;  // time of the rising edge
    static int last_state;             // previous pin state
    int state = digitalRead(pin);      // current pin state
    unsigned long pulse_length = 0;    // default return value

    // On rising edge: record current time.
    if (last_state == LOW &amp;&amp; state == HIGH) {
        rising_time = micros();
    }

    // On falling edge: report pulse length.
    if (last_state == HIGH &amp;&amp; state == LOW) {
        unsigned long falling_time = micros();
        pulse_length = falling_time - rising_time;
    }

    last_state = state;
    return pulse_length;
}


A better approach, but harder to implement for a novice, is to use
interrupts. Chris gave you a pointer to Nick Gammon’s tutorial on
interrupts. This is definitely a good reading. But you will also have to
dig into the data sheet of the ATmega328P microcontroller.
It's a little bit daunting, but a must read if you go into low-level
programming. If you follow this path, I suggest you look at the “input
capture” capability of Timer 1 (chapter 20, section 20.9). This is a
feature of the timer that can have it automatically save a timestamp
whenever some external pin changes state. You then use the input capture
interrupt to alert the program when an edge has been detected, and your
interrupt service routine can read the automatically saved timestamp.
Using this approach, you can have extremely accurate pulse length
measurements, independent on how fast your program goes through its
loop().