Why doesn't the catch in async await code fire? - javascript

is there any .catch() method like there is with Promises for async await style of code?
Here's an example of a code written via Promise:
const apiURL = 'https://jsonplaceholder.typicode.com/todos/1';
const badURL = 'zhttps://wcaf.fajfkajf.gg'
function getData(url){
fetch(url)
.then(response => response.json())
.then(json => console.log(json))
.catch( err => console.log('cannot load api'))
}
getData(apiURL);
getData(badURL);
A simple function to try to load data and if not, display a basic error message. Now I was trying to transcribe this into async/await style code, issue was, I could not really figure out a way to write this with catch()
My best guess was to try try - catch but the catch part doesn't work:
const apiURL = 'https://jsonplaceholder.typicode.com/todos/1';
const badURL = 'zhttps://wcaf.fajfkajf.gg'
async function getData(url){
const response = await fetch(url);
try {
const json = await response.json();
console.log(json);
} catch (e) {
console.log('cannot load api');
}
}
getData(apiURL);
getData(badURL);
This loads the object API just fine, but never seems to go into the catch{} block despite being passed incorrect url.
Any idea what am I doing wrong?

As pointed out in the comments by #l-portet, this is because the code inside try { } block does not actually fail!
.json() will return a promise, regardless of the content of the parsed body text, so even though the initial fetch() fails, you can still call .json() on it - albeit it's completely redundant as it won't return anything meaningful.
Putting the fetch() request inside the try { } block does result in the expected behaviour:
const apiURL = 'https://jsonplaceholder.typicode.com/todos/1';
const badURL = 'zhttps://wcaf.fajfkajf.gg'
async function getData(url){
try {
const response = await fetch(url);
const json = await response.json();
console.log(json);
} catch (e) {
console.log('cannot load api');
}
}
getData(apiURL);
getData(badURL);

One thing you should be aware is that when an async function is executed, it always returns a promise, regardless the exit condition of the function.
If the function has an explicit return (or completes without crashing) the promise will be resolved to the value it returned (or to undefined if there was no explicit return), if the function throws, the promise will be rejected, passing the thrown error object.
Knowing that you could simply handle the error where you use the function, for example:
const apiURL = 'https://jsonplaceholder.typicode.com/todos/1';
const badURL = 'zhttps://wcaf.fajfkajf.gg'
async function getData(url){
const response = await fetch(url);
return await response.json();
}
getData(apiURL).then(data => console.log(data));
getData(badURL).catch(err => console.log('error:', err));
IMHO handling the error closely where you have a use-case of the function makes more sense, since normally when you expect to have an error is because we have a way to handle it (maybe try another API url in this example).
One pattern that I've been using lately is to wrap promises in a way they resolve returning a tuple, in the convention of [error, value] (similar to the way the Go programming language handle async error), in that way for instance you could handle the error in the specific getData call, for example:
const apiURL = 'https://jsonplaceholder.typicode.com/todos/1';
const badURL = 'zhttps://wcaf.fajfkajf.gg'
async function getData(url){
const response = await fetch(url);
return await response.json();
}
// simple utility function
const safePromise = promise =>
promise.then(data => [null, data]).catch(err => [err, undefined]);
(async () => {
const [err, json] = await safePromise(getData(apiURL))
if (err) {
// handle the error
}
console.log(json)
const [error, data] = await safePromise(getData(badURL))
if (error) {
console.log('Error:', error);
}
})()
Check the following library which basically ships this pattern:
await-to-js

Related

How to return a promise that resolves customized data from API Javascript

So I'm using the API of National Weather Service to create a weather app. But the fetched data is very complicated, and I need to fetch two different APIs. So I want to write a customized async function that returns a promise, which resolves to an object that contains only the necessary data I need.
I came up with something like this:
async function fetchWeatherAPI(){
//data that I need
let data = {};
try {
const res1 = await fetch(url1);
const result1 = await res1.json();
data = {...data , result1.usefulData};
} catch(error){}
try {
const res2 = await fetch(url2);
const result2 = await res2.json();
data = {...data, result2.usefulData};
} catch(error){}
return new Promise((resolve,reject)=>{
resolve(data);
})
}
This code is working for me. But the problem is, what if the APIs reject? How can I handle the error so that I can display the error message in the returned promise? I may want something like this:
return new Promise((resolve,reject)=>{
if(...) reject(errrorMessage);
resolve(data);
})
Just do return data. You're already inside an async function and the return value from that function will be the resolved value of the promise that the async function already returns:
async function fetchWeatherAPI(){
//data that I need
let data = {};
const res1 = await fetch(url1);
const result1 = await res1.json();
data = {...data , result1.usefulData};
const res2 = await fetch(url2);
const result2 = await res2.json();
data = {...data, result2.usefulData};
return data;
}
In addition, your catch blocks are silently eating errors and attempting to continue as if nothing went wrong. You probably just want the error to propagate back to the caller so if you just remove your try/catch blocks, that's what will happen.
And, the return new Promise() you have is entirely superfluous and unnecessary (referred to as an anti-pattern). You can just remove it and return data instead.
Note: Since the code you show does not show any dependency between your first and second fetch() calls, you could run them in parallel and perhaps finish them faster.
async function fetchWeatherAPI(){
//data that I need
let data = {};
const [result1, result2] = await Promise.all([
fetch(url1).then(r => r.json()),
fetch(url2).then(r => r.json())
]);
return {...data, result1.usefulData, result2.usefulData};
}
Or, I often use a helper function in my code when doing a lot of fetch() calls:
function fetchJson(...args) {
return fetch(...args).then(r => r.json());
}
async function fetchWeatherAPI() {
//data that I need
let data = {};
const [result1, result2] = await Promise.all([
fetchJson(url1),
fetchJson(url2)
]);
return { ...data, result1.usefulData, result2.usefulData };
}

Promise is "pending" even with having ".then" chained, issues only occurs while using normal functions

I am currently trying to use an API to get IP addresses for visitors. I am getting a "promise pending" message when using the following code:
function getIPAdd(){
var ip = fetch('https://pro.ip-api.com/json/?key=KEY').then(response => response.json()).then(data => data);
return ip;
}
var ipAdd = getIPAdd();
console.log(ipAdd);
I get this output:
Promise {<pending>}
__proto__: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: Object
This means that the promise is ebing fulfilled but for some reason, still is not completed?
While if I use the following code:
(function(){
fetch('https://pro.ip-api.com/json/?key=KEY').then(response => response.json()).then(data => console.log(data));
})();
I do not get any "promise pending" issues. I am guessing this is becasue it is due to the way console.log is used in the latter code sample but how can I fix the first code sample so I get my JSON output the way I need it.
You need to resolve the promise that fetch returns. Something like this should work:
function getIPAdd(){
var ip = fetch('https://pro.ip-api.com/json/?key=KEY').then(response => response.json());
return ip;
}
var ipAdd = getIPAdd();
ipAdd.then(data => console.log(data));
Another way to do this is by using the await keyword. The only catch there is that you can only do so from an async function. So here is an example:
const getIP = () => fetch('https://pro.ip-api.com/json/?key=KEY').then(response => response.json())
const main = async () => {
ip = await getIP();
console.log(ip)
}
main();
This happens because at the time of your console.log the Promises haven't been resolved yet, in other words, console.log(ipAdd) runs before return ip.
Fixing this with promises can get complicated, I recommend using the async/await and ES6+ syntax:
const getIPAdd = async () => {
const response = await fetch('https://pro.ip-api.com/json/?key=KEY');
const ip = await response.json();
return ip;
}
const doStuffWithIP = async () => {
const ipAdd = await getIpAdd()
console.log(ipAdd)
// Code to use IP address here
}
doStuffWithIP() // Don't forget to call the function

NodeJS Axios response undefined on console.log API

I try to make this code work.
const axios = require('axios');
let bodyapi = axios.get('there's my api')
console.log(bodyapi.data) <- undefined
let body = bodyapi.data
console.log(body.discord) <- couldn't get parameter ''discord'' of undefined
Response type of the API:
"discord":{"Category":"activation","Qty":1542,"Price":1}
"vkontakte":{"Category":"activation","Qty":133,"Price":21}
I get it ''undefined''. Running on NodeJS.
axios return promise
const axios = require('axios');
// use async await
(async ()=>{
let bodyapi = await axios.get('there's my api')
console.log(bodyapi.data) // response
})()
// other way
axios.get('there's my api').then(data=> console.log(data))
You can chain a then method as axios returns promise.
you can also chain a catch method to catch potential errors.
const axios = require('axios');
axios.get('there's my api').then(bodyapi => {
console.log(bodyapi.data)
let body = bodyapi.data
console.log(body.discord)
}).catch(error => {
console.log(error);
});
hope this helps. GOOD LUCK :)
Axios Returns a promise. You can look at the documentation here
You can either use await to wait for the response in that case you should use a try catch block to make sure you take care of the errors from your discord Endpoint. This is a good read on error handling for asnyc/wait (here)
Like #arshpreet suggested
(async ()=>{
try{
let bodyapi = await axios.get('there's my api')
console.log(bodyapi.data) // response
} catch(error){
console.error(error)
}
})()
Or you can do it then and catch to take care of the errors.
just like waleed has mentioned.

Node JS Asyncrounous Function Doesn't Return the Right Result

Have this module.exports file which returns a function to fetch some profiles using the gender parameter as the body of the request.
Here, the function is asynchronous and waits for the fetch to return a result so it can proceed. I am using all the async js rules but still, it returns undefined.
I know that there isn't any problem in the URL or the API endpoint because I directly console logged it in .then() the promise returned by the fetch, it firstly consoles logs undefined and then it returns the original value.
Here's the code:
// Pre Configuration
const fetch = require('node-fetch')
module.exports = async (req, res, genderCode) => {
const apiURL = req.apiURL
const requestURL = `${apiURL}/featured?gender=${genderCode}`
await fetch(requestURL)
.then(res => res.json())
.then(data => {
return data._embedded.compactProfiles
})
}
Also where I call the function, I also use await there.
Can anybody tell what's wrong with it?
You haven't put a return statement in the anonymous function you export.
You await the value from the second then statement (although I'm at a loss as to why you are using then and async/await), and then do nothing with it.
module.exports = async (req, res, genderCode) => {
const apiURL = req.apiURL
const requestURL = `${apiURL}/featured?gender=${genderCode}`
const res = await fetch(requestURL);
const data = await res.json();
return data._embedded.compactProfiles
}

Save Async/Await response on a variable

I am trying to understand async calls using async/await and try/catch.
In the example below, how can I save my successful response to a variable that can be utilized throughout the rest of the code?
const axios = require('axios');
const users = 'http://localhost:3000/users';
const asyncExample = async () =>{
try {
const data = await axios(users);
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
//Save response on a variable
const globalData = asyncExample();
console.log(globalData) //Promise { <pending> }
1) Return something from your asyncExample function
const asyncExample = async () => {
const result = await axios(users)
return result
}
2) Call that function and handle its returned Promise:
;(async () => {
const users = await asyncExample()
console.log(users)
})()
Here's why should you handle it like this:
You can't do top-level await (there's a proposal for it though);
await must exist within an async function.
However I must point out that your original example doesn't need async/await
at all; Since axios already returns a Promise you can simply do:
const asyncExample = () => {
return axios(users)
}
const users = await asyncExample()
try..catch creates a new block scope. Use let to define data before try..catch instead of const, return data from asyncExample function call
(async() => {
const users = 123;
const asyncExample = async() => {
let data;
try {
data = await Promise.resolve(users);
} catch (err) {
console.log(err);
}
return data;
};
//Save response on a variable
const globalData = await asyncExample();
console.log(globalData);
// return globalData;
})();
I had same issue with you and found this post. After 2 days of trying I finally found a simple solution.
According to the document of JS, an async function will only return a Promise object instead of value. To access the response of Promise, you have to use .then()method or await which can return the resulting object of Promise is instead of Promise itself.
To change variables from await, you have access and change the variable you want to assign within the async function instead of return from it.
//Save response on a variable
var globalData;
const asyncExample = async () =>{
try {
const data = await axios(users);
globalData = data; // this will change globalData
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
asyncExample();
But if you do this, you may get an undefined output.
asyncExample();
console.log(globalData) //undefined
Since asyncExample() is an async function, when console.log is called, asyncExample() has not finished yet, so globalData is still not assigned. The following code will call console.log after asyncExample() was done.
const show = async () => {
await asyncExample();
console.log(globalData);
}
show();
Because the events are happening asynchronously you need to tie in a callback/promise. I'm going to assume it returns a promise.
const axios = require('axios');
const users = 'http://localhost:3000/users';
const asyncExample = async () =>{
try {
const data = await axios(users);
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
//Save response on a variable
const globalData = asyncExample().then( (success, err) => {
if (err) { console.error(err); }
console.log(success)
}
Just use a callback/promise (cascading programming):
axios(users).then(function(response) {
const globalData = response;
console.log(globalData)
});

Categories

Resources