Reputation: 11
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
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