If you have landed here so you are dealing with JavaScript async(Asynchronous) development. this article will explain a nice way of implementing timeout mechanism for a async function or any object which inherit from Promise object.
Issue
In a sunny day a basic asynchronous function is something like this :
function run(){
return new Promise((resolve,reject)=>{
//the task is going to take 2 seconds to be completed or it doesn’t matter we can wait for it’s completion
setTimeout(()=>{
resolve();
},2000);
});
}
console.log("begin at " + new Date().getTime()/1000);
run().then(()=>{
console.log("end at " + new Date().getTime()/1000);
});
//Output
begin at 1550329812.264
end at 1550329814.267
But the issue is eventually you will face with a situation like this:
function run(){
return new Promise(async(resolve,reject)=>{
//a task which may take n seconds to finish (0<n<?)
//in another word, we don't know how long it will take
await delay(100000)
resolve();
});
}
async function delay(time) {
return new Promise(function (resolve) {
setTimeout(resolve, time);
});
}
console.log("begin at " + new Date().getTime()/1000);
run().then(()=>{
console.log("end at " + new Date().getTime()/1000);
});
//output
begin at 1550332497.638
as it can be seen in above extreme example, when we don’t know the duration of the task’s execution or if we don’t have the luxury of waiting “forever” for the task to be finalized we will begin to think of a mechanism which can help us define a function with timeout functionality.
Solution
In the first sample below
async function run(timeout){
let ret= new Promise(async(resolve,reject)=>{
setTimeout(() => {
if (!ret.isResolved){
reject();
}
}, timeout);
//a task which may take n seconds to finish (0<n<?)
//in another word, we don't know how long it will take
await delay(2000)
resolve();
});
return ret;
}
async function delay(time) {
return new Promise(function (resolve) {
setTimeout(resolve, time);
});
}
console.log("begin at " + new Date().getTime()/1000);
run(3000).then(function(){
console.log("end at " + new Date().getTime()/1000);
}).catch(function(){
console.log("timeout at " + new Date().getTime()/1000);
});
//output
begin at 1550339001.759
end at 1550339003.763
the duration has been set to 2000 milliseconds which is lesser than our timeout value which is 3000 milliseconds, therefore the task will finish after 2000 milliseconds successfully but now let’s see what will happen if we set the duration of the task to a greater value to make the point of this article in the code below
async function run(timeout){
let ret= new Promise(async(resolve,reject)=>{
setTimeout(() => {
if (!ret.isResolved){
reject();
}
}, timeout);
//a task which may take n seconds to finish (0<n<?)
//in another word, we don't know how long it will take
await delay(100000)
resolve();
});
return ret;
}
async function delay(time) {
return new Promise(function (resolve) {
setTimeout(resolve, time);
});
}
console.log("begin at " + new Date().getTime()/1000);
run(3000).then(function(){
console.log("end at " + new Date().getTime()/1000);
}).catch(function(){
console.log("timeout at " + new Date().getTime()/1000);
});
//output
begin at 1550339522.407
timeout at 1550339525.41
As it can be seen we have successfully implemented a timeout mechanism, the logic is simple and reliable an simply can be shown below
async function run(timeout){
let ret= new Promise(async(resolve,reject)=>{
setTimeout(() => {
if (!ret.isResolved){
reject();
}
}, timeout);
await logRunningTask();
resolve();
});
return ret;
}
run(timeoutValue).then(success).catch(timeout);
I hope this mechanism will be useful for you as it was for me, I have found a lot of complicated approaches which had motivated me to come up with a simple and reliable solution :)