Skip to content Skip to sidebar Skip to footer

How To Implement A "function Timeout" In Javascript - Not Just The 'settimeout'

How to implement a timeout in Javascript, not the window.timeout but something like session timeout or socket timeout - basically - a 'function timeout' A specified period of time

Solution 1:

I'm not entirely clear what you're asking, but I think that Javascript does not work the way you want so it cannot be done. For example, it cannot be done that a regular function call lasts either until the operation completes or a certain amount of time whichever comes first. That can be implemented outside of javascript and exposed through javascript (as is done with synchronous ajax calls), but can't be done in pure javascript with regular functions.

Unlike other languages, Javascript is single threaded so that while a function is executing a timer will never execute (except for web workers, but they are very, very limited in what they can do). The timer can only execute when the function finishes executing. Thus, you can't even share a progress variable between a synchronous function and a timer so there's no way for a timer to "check on" the progress of a function.

If your code was completely stand-alone (didn't access any of your global variables, didn't call your other functions and didn't access the DOM in anyway), then you could run it in a web-worker (available in newer browsers only) and use a timer in the main thread. When the web-worker code completes, it sends a message to the main thread with it's results. When the main thread receives that message, it stops the timer. If the timer fires before receiving the results, it can kill the web-worker. But, your code would have to live with the restrictions of web-workers.

Soemthing can also be done with asynchronous operations (because they work better with Javascript's single-threaded-ness) like this:

  1. Start an asynchronous operation like an ajax call or the loading of an image.
  2. Start a timer using setTimeout() for your timeout time.
  3. If the timer fires before your asynchronous operation completes, then stop the asynchronous operation (using the APIs to cancel it).
  4. If the asynchronous operation completes before the timer fires, then cancel the timer with clearTimeout() and proceed.

For example, here's how to put a timeout on the loading of an image:

functionloadImage(url, maxTime, data, fnSuccess, fnFail) {
    var img = newImage();

    var timer = setTimeout(function() {
        timer = null;
        fnFail(data, url);
    }, maxTime);

    img.onLoad = function() {
        if (timer) {
            clearTimeout(timer);
            fnSuccess(data, img);
        }
    }

    img.onAbort = img.onError = function() {
        clearTimeout(timer);
        fnFail(data, url);
    }
    img.src = url;
}

Solution 2:

You could execute the code in a web worker. Then you are still able to handle timeout events while the code is running. As soon as the web worker finishes its job you can cancel the timeout. And as soon as the timeout happens you can terminate the web worker.

execWithTimeout(function() {
    if (Math.random() < 0.5) {
        for(;;) {}
    } else {
        return12;
    }
}, 3000, function(err, result) {
    if (err) {
        console.log('Error: ' + err.message);
    } else {
        console.log('Result: ' + result);
    }
});

functionexecWithTimeout(code, timeout, callback) {
    var worker = newWorker('data:text/javascript;base64,' + btoa('self.postMessage((' + String(code) + '\n)());'));
    var id = setTimeout(function() {
        worker.terminate();
        callback(newError('Timeout'));
    }, timeout);
    worker.addEventListener('error', function(e) {
        clearTimeout(id);
        callback(e);
    });
    worker.addEventListener('message', function(e) {
        clearTimeout(id);
        callback(null, e.data);
    });
}

Solution 3:

I realize this is an old question/thread but perhaps this will be helpful to others.

Here's a generic callWithTimeout that you can await:

exportfunctioncallWithTimeout(func, timeout) {
  returnnewPromise((resolve, reject) => {
    const timer = setTimeout(() =>reject(newError("timeout")), timeout)
    func().then(
      response =>resolve(response),
      err =>reject(newError(err))
    ).finally(() =>clearTimeout(timer))
  })
}

Tests/examples:

exportfunctionsleep(ms) {
  returnnewPromise(resolve =>setTimeout(resolve, ms))
}

const func1 = async () => {
  // test: func completes in timeawaitsleep(100)
}

const func2 = async () => {
  // test: func does not complete in timeawaitsleep(300)
}

const func3 = async () => {
  // test: func throws exception before timeoutawaitsleep(100)
  thrownewError("exception in func")
}

const func4 = async () => {
  // test: func would have thrown exception but timeout occurred firstawaitsleep(300)
  thrownewError("exception in func")
}

Call with:

try {
  awaitcallWithTimeout(func, 200)
  console.log("finished in time")
}
catch (err) {
  console.log(err.message)  // can be "timeout" or exception thrown by `func`
}

Solution 4:

You can achieve this only using some hardcore tricks. Like for example if you know what kind of variable your function returns (note that EVERY js function returns something, default is undefined) you can try something like this: define variable

var x = null;

and run test in seperate "thread":

functiontest(){
    if (x || x == undefined)
        console.log("Cool, my function finished the job!");
    elseconsole.log("Ehh, still far from finishing!");
}
setTimeout(test, 10000);

and finally run function:

x = myFunction(myArguments);

This only works if you know that your function either does not return any value (i.e. the returned value is undefined) or the value it returns is always "not false", i.e. is not converted to false statement (like 0, null, etc).

Solution 5:

My question has been marked as a duplicate of this one so I thought I'd answer it even though the original post is already nine years old.

It took me a while to wrap my head around what it means for Javascript to be single-threaded (and I'm still not sure I understood things 100%) but here's how I solved a similar use-case using Promises and a callback. It's mostly based on this tutorial.

First, we define a timeout function to wrap around Promises:

consttimeout = (prom, time, exception) => {
    let timer;
    returnPromise.race([
        prom,
        newPromise((_r, rej) => timer = setTimeout(rej, time, exception))
    ]).finally(() =>clearTimeout(timer));
}

This is the promise I want to timeout:

const someLongRunningFunction = async () => {
  ...
  return ...;
}

Finally, I use it like this.

constTIMEOUT = 2000;
const timeoutError = Symbol();
var value = "some default value";
try {
  value = awaittimeout(someLongRunningFunction(), TIMEOUT, timeoutError);
}
catch(e) {
  if (e === timeoutError) {
    console.log("Timeout");
  }
  else {
    console.log("Error: " + e);
  }
}
finally {
  returncallback(value);
}

This will call the callback function with the return value of someLongRunningFunction or a default value in case of a timeout. You can modify it to handle timeouts differently (e.g. throw an error).

Post a Comment for "How To Implement A "function Timeout" In Javascript - Not Just The 'settimeout'"