David Tu
David Tu

Reputation: 11

how can i run async function with timeout mechanism

after runTaskWithTimeout(), how can i print 'task finally' successfully after 'task timeout error happen'?

thanks.

async function runTaskWithTimeout() {
    try {
        const timeout = setTimeout(() => {
            console.log('task timeout error happen')
            throw new Error('timeout error happened')
        }, 2000)
        console.log('task start')

        await wait(3000);
        /** assume this is async function*/

        clearTimeout(timeout);
        console.log('task complete')
    } catch (error) {
        console.log('catch error', error.message);
    } finally {
        console.log('task finally')
    }
}

async function wait(ms) {
      return new Promise(resolve => {
         setTimeout(() => { resolve(ms);}, ms);
     });
}

Upvotes: 1

Views: 3357

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1073969

You can readily do a timeout with Promise.race. Here's a function that (like your wait function) settles a promise after a delay, but unlike wait, it rejects the promise with an error containing the given message:

const timeout = (ms, message) => {
    return new Promise((_, reject) => {
        setTimeout(() => {
            reject(new Error(message));
        }, ms);
    });
};

Then in runTaskWithTimeout, you race that timeout with the actual work:

async function runTaskWithTimeout() {
    console.log("Running");
    try {
        await Promise.race([
            timeout(3000), // 3000 = the maximum time to wait
            (async () => {
                // ...do the real work, modelled here as `wait`...
                await wait(2000);
            })()
        ]);
        console.log("task complete")
    } catch (error) {
        console.log("catch error", error.message);
    } finally {
        console.log("task finally")
    }
}

Promise.race settles its promise with the result of the first promise you pass it to settle. So if the actual work is finished (or fails) before the timeout, that's the result of the promise from Promise.race. If the timeout happens before the actual work is complete, the timeout is the result from Promise.race.

Live Example using random values for timeout and wait so sometimes the timeout happens before the work is done, and other times it doesn't:

const wait = ms => new Promise(resolve => setTimeout(resolve, ms));

const timeout = (ms, message) => {
    return new Promise((_, reject) => {
        setTimeout(() => {
            reject(new Error(message));
        }, ms);
    });
};

async function runTaskWithTimeout() {
    console.log("Running");
    try {
        // Just for the purposes of example, let's make it random which
        // comes first:
        const timeoutLength = Math.floor(Math.random() * 1000);
        const waitLength    = Math.floor(Math.random() * 1000);
        await Promise.race([
            timeout(timeoutLength),
            (async () => {
                // This is the real work
                await wait(waitLength);
            })()
        ]);
        console.log("task complete")
    } catch (error) {
        console.log("catch error", error.message);
    } finally {
        console.log("task finally")
    }
}

document.getElementById("run").addEventListener("click", () => {
    runTaskWithTimeout();
});
<input type="button" id="run" value="Run">

Note: The above is just a sketch. In a full implementation, you'd want a way for the "real work" to know the timeout had occurred so it could stop whatever it was doing. You'd probably want to use an AbortController and AbortSignal for that (or equivalents in your environment if you're not doing web code). And you'd probably want runTaskWitHTimeout to return the fulfillment value of the "real work" (which it could do in the above by just returning the value from await Promise.race in the non-error branch).

Upvotes: 4

Related Questions