Reputation: 1218
I want to set a limit time for a function to be executed, if it can not finish its job at that time, then the function execution should be aborted. What ways I have to deal with it. I thought I would create a "setTimeout" for each function with run time, but I can not think of a way to abort the execution of the function, unless it is inside a "setTimeout" which I could give a " cleanTimeout "and abort it, but can think of other ways to do this?
Example
Timeout:
const eventBus = new EventEmitter();
function Timeout(parent) {
this.id = undefined;
this.parent = parent;
}
Timeout.prototype.set = function(limitTimeMs) {
const parent = this.parent;
this.id = setTimeout(function() {
eventBus.emit('runEnd', parent);
}, limitTimeMs);
}
Timeout.prototype.clear = function() {
clearTimeout(this.id);
}
Runner:
function Runner() {
this.tests = [];
eventBus.on('runEnd', function(test) {
console.log('runEnd');
// results ready for the preview layer...
});
eventBus.on('testEnd', function(test) {
console.log('testEnd');
});
}
Runner.prototype.addTest = function(test) {
this.tests.push(test);
}
Runner.prototype.run = function() {
for(let i = 0; i < this.tests.length; i++)
this.tests[i].run();
}
Test:
function Test(fn, limit) {
this.fn = fn;
this.limit = limit;
this.timeout = new Timeout(this);
}
Test.prototype.run = function() {
this.fn.call(this);
this.timeout.clear();
eventBus.emit('testEnd', this);
}
Main:
const runner = new Runner();
const test1 = new Test(function() {
this.timeout.set(1);
let i = 0;
while(i < 1000000000)
i++;
});
runner.addTest(test1);
runner.run();
Practical Example:
http://playcode.io/197465?tabs=console&script.js&output
I've simplified the maximum, I also have to deal with "Test" asynchronous. The problem is that I can configure up to "this.timeout.set(0);" that still "fn();" is executed first, and even though it takes a long while in the "while" above, still "this.timeout.set(0);" is run after it. I understand that there is a minimum, I think "4ms" (depends on the browser) for a "setTimeout" to be called, what alternatives do I have to be able to handle it? Note that I can not modify the "fn" function, just add code before or after it.
I realized that what I was trying to do was absurd for a single thread, of course it might work, if "setTimout(0)" was executed immediately, but there would still be the problem that the test function would continue its execution, and that is a very ugly way of dealing with the problem. I found another way, because I wanted to avoid a "stackoverflow" error, so I made a move based on the @blex example with Web Worker, it would work, but then I thought, why do I want to avoid a "stackoverflow"? Would not that be bad? how to know if it would cause the error or not, it could be confusing, but still I continued, because I needed to know if I could do it, here it is and it works:
http://playcode.io/198133?tabs=console&index.html&output
But I thought better, I do not want to avoid a "stack exceeded" error, if it happens, it's best that I know it. So I'll deal otherwise, which is simpler, I'll pick up the initial time, then the time at the end of the test, compare the duration with the timeout, if it has exceeded, then I make a mistake. The problem, which I do not see as a problem anymore, is that the function will be executed to the end to check if the time has exceeded, and thus not being able to escape a "stack exceeded" error.
Upvotes: 1
Views: 1804
Reputation: 1
function someFunction() {
// Code for your function
}
function someFunctionWithTimeout(timeout) {
return Promise.race([
someFunction(),
new Promise((resolve, reject) =>
setTimeout(() => reject(new Error('Timeout')), timeout)
)
]);
}
In JavaScript, you can use the Promise.race() method to implement a timeout mechanism for a function call. The Promise.race() method takes an array of promises as input and returns a new promise that resolves or rejects as soon as one of the input promises resolves or rejects. You can use this method to create a promise that resolves if the function call completes within the time limit, and rejects if it takes too long.
Upvotes: 0
Reputation: 138234
You could use a generator to yield the execution inbetween, then check if time is over:
function runMax(fn, max) {
const endAt = Date.now() + max;
let done, iterator = fn();
do {
({ done } = iterator.next());
if(Date.now() >= endAt) return;
} while(!done)
}
console.log("start");
runMax(function* () {
while(true) {
// some long running code
console.log("do stuff");
// sometimes yield inbetween:
yield;
}
}, 1000);
console.log("aborted");
Upvotes: 2