Catching errors thrown from nested asynchronous functions at the top-level - javascript

I am having trouble "catching" an error using either a try/catch or .catch statement, and I believe it is because the error is not being thrown in a "conventional" manner. At the top-level, I have an invocation of a function within an async function:
async function main() {
let computedValue;
try {
computedValue = await module.doComputation();
} catch (err) {
console.log("We caught the error!")
computedValue = null;
}
}
// in `module`
import $ from 'jquery';
export function doComputation() {
return new Promise((resolve) => {
$.ajax({
type: 'GET',
url: location.href,
success: (data) => {
const text = $($.parseHTML(data)).find('#some-dom-element').text()
const total = text.match(/[+-]?\d+(\.\d+)?/g)[0]; // this line throws an error!
if (total > 0) {
resolve(total);
}
},
error: () => {
resolve(-1);
},
});
});
}
When I run this, the exception that is thrown from within doComputation is unhandled, and it looks like:
Uncaught TypeError: Cannot read properties of null (reading '0')
I am not particularly surprised that this function threw an error (and I understand why), however I need to be sure that main can safely catch the error that this function throws.
Importantly, I would really prefer to be able to catch this error at the level of main (technically there are hundreds of different implementations of doComputation in my project and having to modify all of them would be a tad difficult).
Does anyone have any ideas about how I might solve this problem? Adding a .catch to await module.doComputation does not seem to do the trick and also makes my LSP unhappy.
I tried adding a .catch statement to the offending line, and also wrapping the entire statement in a try/catch, but to no avail.

You should include a reject variable in your promise and handle the error that way
return new Promise((resolve,reject) => {
$.ajax({
type: 'GET',
url: location.href,
success: (data) => {
const text = $($.parseHTML(data)).find('#some-dom-element').text()
const total = text.match(/[+-]?\d+(\.\d+)?/g)[0]; // this line throws an error!
if (total > 0) {
resolve(total);
}
},
error: () => {
reject(-1)
},
});
});
}
And in main
function main() {
let computedValue;
module.doComputation().then((res) => {
computedValue = res
}).catch(()=>{
console.log("We caught the error!")
computedValue = null;
})
}
Hope this helps!

Related

Javascript condition to use await of not

Is there a way I can write this block of code without all the repeated code for calling axios?
export const handler = async (event, useAwait) => {
const { path, data } = JSON.parse(event);
if (useAwait) {
return await axios(`https://example.com/api${path}`, {
method: 'post',
headers: {
'api-key': key,
},
data: data,
});
} else {
// non-blocking
axios(`https://example.com/api${path}`, {
method: 'post',
headers: {
'api-key': key,
},
data: data,
});
return true;
}
};
Put the promise into a variable, and then you can conditionally return it.
export const handler = async (event, useAwait) => {
const { data } = JSON.parse(event);
const prom = axios(`https://example.com/api${url}`, {
method: 'post',
headers: {
'api-key': key,
},
data: data,
});
return useAwait ? (await prom) : true;
};
That said, you may as well return the Promise itself - awaiting something that you're returning immediately doesn't help since you're not immediately inside a try.
return useAwait ? prom : true;
But calling this function without returning the result looks like a bad idea because then you may get an unhandled rejection. You may wish to add a .catch to the Promise in that case to avoid that.
export const handler = (event) => {
const { path, data } = JSON.parse(event);
const prom = axios(`https://example.com/api${path}`, {
method: 'post',
headers: {
'api-key': key,
},
data: data,
});
prom.catch(()=>{}); // prevent uncaught rejection
return prom;
}
If you don't test the return value of handler to be strictly equal to true, calling code can rely on promise objects being truthy in a conditional expression as needed, and the logic can be simplified to an ordinary function that returns a promise without using await or taking a useAwait parameter.
The dummy catch handler added to prom is to prevent generating an uncaught-rejected-promise error if the caller doesn't handle rejection of the axios promise, and can be omitted if uncaught rejection errors are acceptable. How calling code is meant to operate without waiting for the axios call completed is unclear and not covered here.
One of the reasons for suggesting major simplifications is the // non-blocking comment in the posted code. Async functions always return a promise object when called with no blocking effect, either to continued execution of calling code or browser operation.
The only effective difference between using await or not (in the post) is the value used to resolve the returned promise by handler.

how to do ajax call recursively, to check if service is available

I am trying to do ajax call recursively to check whether web service is available or not.
Here is my code
function checkWebService() {
console.log("function called")
$.ajax({
url: "localhost:8085/login/checkWebServiceIsRunning",
type: 'GET',
success: (response) => {
console.log("success")
},
error: (jqXHR, textStatus, errorThrown) => {
console.log("error")
setTimeout(() => {
checkWebService()
}, 1000)
}
})
}
As my service is not running initially, the code from error block gets executed but after two or three times, the ajax call gets stuck, it neither goes in success nor in error.
Then I tried putting timeout in ajax code, so it gave timeout error, but even if the web service is available and server side code gets executed without any delay, ajax gives timeout issue.
Can someone help on what could be going wrong here.
You can use a while loop and async/await to get the response.
Also, you can add a variable count for the attempt to prevent infinite loop.
Furthermore, a wait function is added to delay each call to not utilize the program.
Note: errorCode:789 is just an example to catch the max attempt error. you can define your own.
const wait = ms => new Promise((resolve, reject) => setTimeout(resolve, ms));
async function checkWebService() {
const MAX_ATTEMPT = 20;
let attempt = 0;
while (true) {
try {
attempt++;
if (attempt > MAX_ATTEMPT) throw { errorCode: "789" };
const response = await $.ajax({
url: "localhost:8085/login/checkWebServiceIsRunning",
type: "GET",
});
return response;
} catch (err) {
if (err.errorCode === "789") {
throw new Error("Exceed Max. attempt.");
}
console.log(err);
await wait(1000);
continue;
}
}
}
checkWebService().then(console.log).catch(console.log);

How Can I Write a Test for a Javascript Asynchronous method like below usning jest

Please, I would appreciate if I can get a help out here to find a suitable test for the function below. It works perfectly fine in my project but I just cannot figure out why my test won't work. Thank you guys...
const GetScore = (() => {
const all = async () => {
const response = await fetch('https://us-central1-js-capstone-backend.cloudfunctions.net/api/games/ZUi2Xo2RRfSKd14twwPn/scores/', {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
});
const data = await response.json();
return data;
};
return {
all,
};
})();
module.exports = GetScore;
My test is as follows
const GetScore = require('../modules/getScore');
let results = false;
it('returns an array of objects with all the scores', () => {
GetScore.all().then((response) => {
if (Array.isArray(response.result) === true) results = true;
expect(results).toBeTruthy();
});
});
The error I get is like this:
node:internal/process/promises:246
triggerUncaughtException(err, true /* fromPromise */);
^
[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "ReferenceError: fetch is not defined".] {
code: 'ERR_UNHANDLED_REJECTION'
Thank you guys in advance
Your test is not asynchronous so it is not waiting for the call to finish. Add an async/await in your test.
it('returns an array of objects with all the scores', async () => {
await GetScore.all().then((response) => {
if (Array.isArray(response.result) === true) results = true;
expect(results).toBeTruthy();
});
});
You should also write tests for the failures, so you might want to wrap the await in a try/catch.

Axios instance promise.all error handling

I have the following code in my own async function that uses another imported function from module which is a custom wrap of axios inside try/catch block:
async function getCharacter (realmSlug, characterName) {
try {
const [{id, name, gender, faction, race, character_class, active_spec, realm, guild, level, last_login_timestamp, average_item_level, equipped_item_level}, {pets, unlocked_battle_pet_slots},{mounts}] = await Promise.all([
getCharacterSummary(realmSlug, characterName), -- custom axios instance
getCharacterPetsCollection(realmSlug, characterName),
getCharacterMountsCollection(realmSlug, characterName)
])
....
return result;
} catch (error) {
console.log(error.code);
if (error.response.status === 404 || error.response.status === 403) {
console.error(`${getCharacter.name},${characterName}#${realmSlug}`);
}
return { name: characterName, realm: realmSlug }
}
}
The problem is that if I use promise.all according to Stackoverflow 1,2 I can not handle errors. So the problem is when I call function to execute, my errors doesn't handle in (catch) block. At all. Even if I don't need print them, anyway I receive messages in console about 404 errors, but console.log(error.code) still gives me nothing. For example:
So is there any way to handle this annoying error messages in console somehow?
For example using .catch somewhere? Or using for await ... of or rxJS instead if it's possible?
Exporting function and using .catch
Even if I export this function getCharacter in another .js file and use the following code:
const getCharacter = require('./getCharacter');
let bulkCharacters = [{realmSlug, characterName},{realmSlug, characterName},... ,n] //array of characters for getCharacter request
const promises = bulkCharacters.map(async ({realmSlug, characterName}) => {
try {
return await getCharacter(realmSlug, characterName);
} catch (e) {
console.log(e)
}
});
let test = await Promise.all(promises)
.catch(function(arrayOfPromises, err) {
// log that I have an error, return the entire array;
console.log('A promise failed to resolve', err);
return arrayOfPromises;
})
.then(function(arrayOfPromises) {
console.log(arrayOfPromises)
})
;
console.log('stop')
I still receive errors in console, without triggering catch block inside getCharacter function or this file in which this function was imported and catch block is outside the function.

RxJS error handling

I've got an issue with RxJS swallowing errors. So in my case I've got something like this:
function funcThatThrowsError(fn, ..args) {
return fn.bind(fn, ...args);
}
function fetchItems() {
Observable.fromPromise(
reqwest({
url: `${API_ROOT}${url}`,
method,
data,
contentType: "application/json"
})
).map(funcThatThrowsError(null, "someValue"))
}
const observableA = fechItems();
const observableB = ... ;
Observable
.combineLatest(
observableA,
observableB,
() => { }
)
.forEach(
() => { }, // success
(err) -> console.log(err.stack);
)
So basically I'm deliberately passing a null value as the fn parameter which causes the funcThatThrowsError to throw an exception.
The problem is that the error callback is not invoked in this case. Rather RxJS will use it's own thrower function
function thrower(e) {
throw e;
}
What's the best practise to deal with this scenario. I feel that there is something that I'm missing.
The exception is happening outside of the Observable. You are raising it while creating a function that you want to pass into the observable.
If you want the error handler at the end of the chain to handle it, you have to raise it inside of the operator chain:
function fetchItems() {
Observable.fromPromise(
request({
url: `${API_ROOT}${url}`,
method,
data,
contentType: "application/json"
})
).map(funcThatThrowsError(content => throw new Error(content), "someValue"))
}

Categories