Cancelling IDBOpenDBRequest? - javascript

I have the following snippet of code:
function open_db(dbname, dbversion, upgrade, onblocked) {
if (upgrade === undefined) {
upgrade = function basic_init(ev) {
…
};
}
if (onblocked === undefined) {
onblocked = function onblocked(ev) {
throw ev;
};
}
let req = window.indexedDB.open(dbname, dbversion);
return new Promise((resolve, reject) => {
req.onsuccess = ev => resolve(ev.target.result);
req.onerror = ev => reject(ev.target.error);
req.onupgradeneeded = ev => {
try {
return upgrade(ev);
} catch (error) {
reject(error);
ev.target.onsuccess = ev => ev.target.close(); // IS THIS LINE NECESSARY?
throw error; // IS THIS LINE UNNECESSARY?
}
};
req.onblocked = ev => {
try {
return onblocked(ev);
} catch (error) {
reject(error);
ev.target.onsuccess = ev => ev.target.close(); // IS THIS LINE NECESSARY?
throw error; // IS THIS LINE UNNECESSARY?
}
};
});
}
If the .onblocked or .onupgradeneeded handlers throw a native error, will that cancel the open attempt? Or will the IDBOpenDBRequest object ignore such errors and steam on ahead obliviously until I manually close the db if/after it's opened?
In a nutshell: are the commented lines of code necessary? Are they sufficient to prevent a dangling open handle?
Is there a better way to cancel the request-to-open, rather than just adding .onsuccess = ev => … .close()?

You're asking the right question ("Is there a better way to cancel the request-to-open... ?") and the answer is: no, not as currently defined/implemented. All you can do is make the open a no-op by aborting the upgrade.
Throwing in a blocked handler doesn't have specified special behavior; doing anything here should be unnecessary, as it will be followed by an upgradeneeded eventually.
On upgradeneeded, closing the connection before the upgrade completes will terminate the request and abort the upgrade, so the version won't change. There are a handful of ways to do this:
call close on the connection (db = e.target.result; db.close();)
Defined by:
https://w3c.github.io/IndexedDB/#open-a-database - If connection was closed...
abort the transaction explicitly (tx = e.target.transaction; tx.abort();)
Defined by: https://w3c.github.io/IndexedDB/#open-a-database - If the upgrade transaction was aborted...
abort the transaction implicitly by throwing within the upgradeneeded event handler.
Defined by: https://w3c.github.io/IndexedDB/#run-an-upgrade-transaction - If didThrow is true...
Note that after seeing upgradeneeded, waiting until success (which your code does) means the transaction will have completed, and the upgrade will have happened.
So in your sample code, the throw statements are effectual (they will abort the upgrade), while the close calls are not. The success event should never fire, in that case, which makes adding handlers for success which close the connection irrelevant.

Related

Multiple javascript promises .then and .catch hitting wrong catch

I think that I am not understanding something properly here as it's very strange behaviour. If I call queryFindPlayer it should be falling into the .then which it does if queryFindContract function is not there but when it is there like below it seems to fall to the queryFindPlayer catch and add a new player.
queryFindPlayer(models, ConsoleId, UserId, SeasonId, LeagueId).then(players => {
const player = players[0];
queryFindContract(db, player.Team.id, UserId, SeasonId, LeagueId).then(contracts => {
console.log("player has a contract to a team");
}).catch(e => {
console.log("failed to find player");
});
}).catch(e => {
queryAddPlayer(models, UserId, TeamId).then(player => {
console.log("added player");
}).catch(addPlayerError => {
console.log("failed to add player, shouldn't happen");
});
});
If queryfindPlayer() resolves so you start execution of the .then() handler, but then you end up in the queryFindPlayer().catch() handler, that can occur for one of the following reasons:
If your code threw an exception before calling queryFindContract() such as if const player = players[0] threw an error of if queryFindContract() wasn't defined.
If your code threw an exception evaluating the arguments to pass queryFindContract() such as player.Team.id throws or any of the other variables you're passing don't exist.
If queryFindContract() throws synchronously before it returns its promise.
If queryFindContract() doesn't return a promise and thus queryfindContract().then() would throw an exception.
All of these will cause a synchronous exception to be thrown in the queryFindPlayer.then() handler which will cause it to go to the queryFindPlayer.catch() handler. It never gets to the queryFindContract().catch() handler because queryFindContract() either never got to execute or because it never got to finish and return its promise.
You can most likely see exactly what is causing your situation by just adding
console.log(e)
at the start of both .catch() handlers. For clarity, also add a descriptive string before the e. such as:
console.log("qfc", e);
and
console.log("qfp", e);
I pretty much always log rejections, even if expecting them sometimes because you can also get rejections for unexpected reasons such as programming errors and you want to be able to see those immediately and not get confused by them.
Thanks for the help, I was not using the exception handler how intended.
queryGetContractById(models, id).then(c => {
return queryFindPlayer(models, ConsoleId, UserId, SeasonId, LeagueId).then(players => {
if(players.length != 0) {
return queryFindContract(models, players[0].Team.id, UserId, SeasonId, LeagueId).then(contracts => {
if(contracts.length != 0) {
console.log("player has a contract to a team");
} else {
queryUpdatePlayersTeam(models, players[0].id, TeamId);
}
});
} else {
return queryAddPlayer(models, UserId, TeamId).then(player => {
console.log("added player");
});
}
});
}).catch(e => {
console.log("failed", e);
});
In my promise, I rejected if there was no data returned and resolving if there was. I can see how this was wrong and I think this is now right.

NodeJs: How to handle delayed errors in streams

I have the following situation.
function emitErrorInStream() {
let resultStream = new PassThrough();
let testStream = new PassThrough();
testStream.on("error", () => {
throw new Error("AA");
}
// the setTimeout simulates what is actually happening in the code.
/*
* actual code
* let testStream = s3.getObject(params).createReadStream();
* if I pass in an incorrect parameter option to the getObject function
* it will be a few milliseconds before an error is thrown and subsequently caught by the stream's error handling method.
*/
setTimeout(() => {testStream.emit("error", "arg");}, 100);
return testStream.pipe(resultStream);
}
try{
let b = emitErrorInStream();
}
catch(err){
console.log(err) // error will not be caught
}
///... continue
I have tried a slew of things to catch the error thrown inside the error handler. I have tried using promises, which never resolve. How can I catch the error thrown inside thetestStream's error handler?
I have found that sending an end event inside the on("error") handler partially solves my issue as it does not crash the application running. It is not a recommended solution https://nodejs.org/api/stream.html#stream_event_end_1
Lastly, is catching this error possible if emitErrorInStream is a third party function to which I do not have access?
Any insights would be greatly appreciated.
// actual typescript code
downloadStream(bucketName: string, filename: string): Stream {
const emptyStream = new PassThrough();
const params = { Bucket: bucketName, Key: filename };
const s3Stream = this.s3.getObject(params).createReadStream();
// listen to errors returned by the service. i.e. the specified key does not exist.
s3Stream.on("error", (err: any) => {
log.error(`Service Error Downloading File: ${err}`);
// Have to emit an end event here.
// Cannot throw an error as it is outside of the event loop
// and can crash the server.
// TODO: find better solution as it is not recommended https://nodejs.org/api/stream.html#stream_event_end_1
s3Stream.emit("end");
});
return s3Stream.pipe(emptyStream);
}

Force stop function execution [duplicate]

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 that will be allowed to elapse in a system
before a specified event is to take place, unless another specified
event occurs first; in either case, the period is terminated when
either event takes place.
Specifically, I want a javascript observing timer that will observe the execution time of a function and if reached or going more than a specified time then the observing timer will stop/notify the executing function.
Any help is greatly appreciated! Thanks a lot.
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:
Start an asynchronous operation like an ajax call or the loading of an image.
Start a timer using setTimeout() for your timeout time.
If the timer fires before your asynchronous operation completes, then stop the asynchronous operation (using the APIs to cancel it).
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:
function loadImage(url, maxTime, data, fnSuccess, fnFail) {
var img = new Image();
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;
}
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:
const timeout = (prom, time, exception) => {
let timer;
return Promise.race([
prom,
new Promise((_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.
const TIMEOUT = 2000;
const timeoutError = Symbol();
var value = "some default value";
try {
value = await timeout(someLongRunningFunction(), TIMEOUT, timeoutError);
}
catch(e) {
if (e === timeoutError) {
console.log("Timeout");
}
else {
console.log("Error: " + e);
}
}
finally {
return callback(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).
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 {
return 12;
}
}, 3000, function(err, result) {
if (err) {
console.log('Error: ' + err.message);
} else {
console.log('Result: ' + result);
}
});
function execWithTimeout(code, timeout, callback) {
var worker = new Worker('data:text/javascript;base64,' + btoa('self.postMessage((' + String(code) + '\n)());'));
var id = setTimeout(function() {
worker.terminate();
callback(new Error('Timeout'));
}, timeout);
worker.addEventListener('error', function(e) {
clearTimeout(id);
callback(e);
});
worker.addEventListener('message', function(e) {
clearTimeout(id);
callback(null, e.data);
});
}
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:
export function callWithTimeout(func, timeout) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => reject(new Error("timeout")), timeout)
func().then(
response => resolve(response),
err => reject(new Error(err))
).finally(() => clearTimeout(timer))
})
}
Tests/examples:
export function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
const func1 = async () => {
// test: func completes in time
await sleep(100)
}
const func2 = async () => {
// test: func does not complete in time
await sleep(300)
}
const func3 = async () => {
// test: func throws exception before timeout
await sleep(100)
throw new Error("exception in func")
}
const func4 = async () => {
// test: func would have thrown exception but timeout occurred first
await sleep(300)
throw new Error("exception in func")
}
Call with:
try {
await callWithTimeout(func, 200)
console.log("finished in time")
}
catch (err) {
console.log(err.message) // can be "timeout" or exception thrown by `func`
}
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":
function test(){
if (x || x == undefined)
console.log("Cool, my function finished the job!");
else
console.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).
Here is my answer which essentially simplifies Martin's answer and is based upon the same tutorial.
Timeout wrapper for a promise:
const timeout = (prom, time) => {
const timeoutError = new Error(`execution time has exceeded the allowed time frame of ${time} ms`);
let timer; // will receive the setTimeout defined from time
timeoutError.name = "TimeoutErr";
return Promise.race([
prom,
new Promise((_r, rej) => timer = setTimeout(rej, time, timeoutError)) // returns the defined timeoutError in case of rejection
]).catch(err => { // handle errors that may occur during the promise race
throw(err);
}) .finally(() => clearTimeout(timer)); // clears timer
}
A promise for testing purposes:
const fn = async (a) => { // resolves in 500 ms or throw an error if a == true
if (a == true) throw new Error('test error');
await new Promise((res) => setTimeout(res, 500));
return "p2";
}
Now here is a test function:
async function test() {
let result;
try { // finishes before the timeout
result = await timeout(fn(), 1000); // timeouts in 1000 ms
console.log('• Returned Value :', result, '\n'); // result = p2
} catch(err) {
console.log('• Captured exception 0 : \n ', err, '\n');
}
try { // don't finish before the timeout
result = await timeout(fn(), 100); // timeouts in 100 ms
console.log(result); // not executed as the timeout error was triggered
} catch (err) {
console.log('• Captured exception 1 : \n ', err, '\n');
}
try { // an error occured during fn execution time
result = await timeout(fn(true), 100); // fn will throw an error
console.log(result); // not executed as an error occured
} catch (err) {
console.log('• Captured exception 2 : \n ', err, '\n');
}
}
that will produce this output:
• Returned Value : p2
• Captured exception 1 :
TimeoutErr: execution time has exceeded the allowed time frame of 100 ms
at C:\...\test-promise-race\test.js:33:34
at async test (C:\...\test-promise-race\test.js:63:18)
• Captured exception 2 :
Error: test error
at fn (C:\...\test-promise-race\test.js:45:26)
at test (C:\...\test-promise-race\test.js:72:32)
If you don't want to use try ... catch instructions in the test function you can alternatively replace the throw instructions in the catch part of the timeout promise wrapper by return.
By doing so the result variable will receive the error that is throwed otherwise. You can then use this to detect if the result variable actually contains an error.
if (result instanceof Error) {
// there was an error during execution
}
else {
// result contains the value returned by fn
}
If you want to check if the error is relative to the defined timeout you will have to check the error.name value for "TimeoutErr".
Share a variable between the observing timer and the executing function.
Implement the observing timer with window.setTimeout or window.setInterval. When the observing timer executes, it sets an exit value to the shared variable.
The executing function constantly checks for the variable value.. and returns if the exit value is specified.

How to find out if WinJS.Promise was cancelled by timeout or cancel() call

I have a server request that is wrapped in a timeout promise.
var pendingRequest = WinJS.Promise.timeout(5000, requestAsync).
The user also has a "Cancel" button on the UI to actively cancel the request by executing pendingRequest.cancel(). However, there is no way to find out that the promise has been cancelled by the user or by the timeout (since timeout calls promise.cancel() internally too).
It would have been nice of WinJS.Promise.timeout would move the promise in the error state with a different Error object like "Timeout" instead of "Canceled".
Any idea how to find out if the request has been cancelled by the timeout?
Update: How about this solution:
(function (P) {
var oldTimeout = P.timeout
P.timeout = function (t, promise) {
var timeoutPromise = oldTimeout(t);
if (promise) {
return new WinJS.Promise(function (c, e, p) {
promise.then(c,e,p);
timeoutPromise.then(function () {
e(new WinJS.ErrorFromName("Timeout", "Timeout reached after " + t + "ms"));
});
});
} else {
return timeoutPromise;
}
};
})(WinJS.Promise);
According to the documentation,
... the promise enters the error state with a value of Error("Canceled")
Thus, error.message === 'Canceled' can be detected in your error handler.
In addition, WinJS.Promise allows an onCancel callback to be specified at construction time.
var promise = new WinJS.Promise(init, onCancel);
where init and onCancel are both functions.
Here's a demo.
Edit
Ah OK, sorry I misread the question. I understand now that you wish to distinguish between a timeout and a manually canceled promise.
Yes, it can be done, by making an appropriate message available to both :
a WinJS promise's onCancel callback
a chained "catch" callback.
First, extend WinJS.Promise.prototype with a .timeout() method :
(function(P) {
P.prototype.timeout = function (t) {
var promise = this;
promise.message = 'Canceled';
P.timeout(t).then(function() {
promise.message = 'Timeout';
promise.cancel();
});
return promise.then(null, function() {
if(error.message == 'Canceled') {
throw new Error(promise.message); //This allows a chained "catch" to see "Canceled" or "Timeout" as its e.message.
} else {
throw error; //This allows a chained "catch" to see a naturally occurring message as its e.message.
}
});
};
})(WinJS.Promise);
This becomes a method of each instance of WinJS.Promise(), therefore does not conflict with the static method WinJS.Promise.timeout() .
Now, use the .timeout() method as follows :
function init() {
//whatever ...
}
function onCancel() {
console.log('onCacnel handler: ' + this.message || `Canceled`);
}
var promise = new WinJS.Promise(init, onCancel);
promise.timeout(3000).then(null, function(error) {
console.log('chained catch handler: ' + error.message);
});
promise.cancel();
/*
* With `promise.cancel()` uncommented, `this.message` and `error.message` will be "Canceled".
* With `promise.cancel()` commented out, `this.message` and `error.message` will be "Timeout".
*/
Demo (with extra code for button animation).

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 that will be allowed to elapse in a system
before a specified event is to take place, unless another specified
event occurs first; in either case, the period is terminated when
either event takes place.
Specifically, I want a javascript observing timer that will observe the execution time of a function and if reached or going more than a specified time then the observing timer will stop/notify the executing function.
Any help is greatly appreciated! Thanks a lot.
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:
Start an asynchronous operation like an ajax call or the loading of an image.
Start a timer using setTimeout() for your timeout time.
If the timer fires before your asynchronous operation completes, then stop the asynchronous operation (using the APIs to cancel it).
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:
function loadImage(url, maxTime, data, fnSuccess, fnFail) {
var img = new Image();
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;
}
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:
const timeout = (prom, time, exception) => {
let timer;
return Promise.race([
prom,
new Promise((_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.
const TIMEOUT = 2000;
const timeoutError = Symbol();
var value = "some default value";
try {
value = await timeout(someLongRunningFunction(), TIMEOUT, timeoutError);
}
catch(e) {
if (e === timeoutError) {
console.log("Timeout");
}
else {
console.log("Error: " + e);
}
}
finally {
return callback(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).
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 {
return 12;
}
}, 3000, function(err, result) {
if (err) {
console.log('Error: ' + err.message);
} else {
console.log('Result: ' + result);
}
});
function execWithTimeout(code, timeout, callback) {
var worker = new Worker('data:text/javascript;base64,' + btoa('self.postMessage((' + String(code) + '\n)());'));
var id = setTimeout(function() {
worker.terminate();
callback(new Error('Timeout'));
}, timeout);
worker.addEventListener('error', function(e) {
clearTimeout(id);
callback(e);
});
worker.addEventListener('message', function(e) {
clearTimeout(id);
callback(null, e.data);
});
}
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:
export function callWithTimeout(func, timeout) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => reject(new Error("timeout")), timeout)
func().then(
response => resolve(response),
err => reject(new Error(err))
).finally(() => clearTimeout(timer))
})
}
Tests/examples:
export function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
const func1 = async () => {
// test: func completes in time
await sleep(100)
}
const func2 = async () => {
// test: func does not complete in time
await sleep(300)
}
const func3 = async () => {
// test: func throws exception before timeout
await sleep(100)
throw new Error("exception in func")
}
const func4 = async () => {
// test: func would have thrown exception but timeout occurred first
await sleep(300)
throw new Error("exception in func")
}
Call with:
try {
await callWithTimeout(func, 200)
console.log("finished in time")
}
catch (err) {
console.log(err.message) // can be "timeout" or exception thrown by `func`
}
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":
function test(){
if (x || x == undefined)
console.log("Cool, my function finished the job!");
else
console.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).
Here is my answer which essentially simplifies Martin's answer and is based upon the same tutorial.
Timeout wrapper for a promise:
const timeout = (prom, time) => {
const timeoutError = new Error(`execution time has exceeded the allowed time frame of ${time} ms`);
let timer; // will receive the setTimeout defined from time
timeoutError.name = "TimeoutErr";
return Promise.race([
prom,
new Promise((_r, rej) => timer = setTimeout(rej, time, timeoutError)) // returns the defined timeoutError in case of rejection
]).catch(err => { // handle errors that may occur during the promise race
throw(err);
}) .finally(() => clearTimeout(timer)); // clears timer
}
A promise for testing purposes:
const fn = async (a) => { // resolves in 500 ms or throw an error if a == true
if (a == true) throw new Error('test error');
await new Promise((res) => setTimeout(res, 500));
return "p2";
}
Now here is a test function:
async function test() {
let result;
try { // finishes before the timeout
result = await timeout(fn(), 1000); // timeouts in 1000 ms
console.log('• Returned Value :', result, '\n'); // result = p2
} catch(err) {
console.log('• Captured exception 0 : \n ', err, '\n');
}
try { // don't finish before the timeout
result = await timeout(fn(), 100); // timeouts in 100 ms
console.log(result); // not executed as the timeout error was triggered
} catch (err) {
console.log('• Captured exception 1 : \n ', err, '\n');
}
try { // an error occured during fn execution time
result = await timeout(fn(true), 100); // fn will throw an error
console.log(result); // not executed as an error occured
} catch (err) {
console.log('• Captured exception 2 : \n ', err, '\n');
}
}
that will produce this output:
• Returned Value : p2
• Captured exception 1 :
TimeoutErr: execution time has exceeded the allowed time frame of 100 ms
at C:\...\test-promise-race\test.js:33:34
at async test (C:\...\test-promise-race\test.js:63:18)
• Captured exception 2 :
Error: test error
at fn (C:\...\test-promise-race\test.js:45:26)
at test (C:\...\test-promise-race\test.js:72:32)
If you don't want to use try ... catch instructions in the test function you can alternatively replace the throw instructions in the catch part of the timeout promise wrapper by return.
By doing so the result variable will receive the error that is throwed otherwise. You can then use this to detect if the result variable actually contains an error.
if (result instanceof Error) {
// there was an error during execution
}
else {
// result contains the value returned by fn
}
If you want to check if the error is relative to the defined timeout you will have to check the error.name value for "TimeoutErr".
Share a variable between the observing timer and the executing function.
Implement the observing timer with window.setTimeout or window.setInterval. When the observing timer executes, it sets an exit value to the shared variable.
The executing function constantly checks for the variable value.. and returns if the exit value is specified.

Categories

Resources