Confused by async behavior - javascript

I'm using a try/catch to make some fetch requests, then I am extracting the title from the HTML and adding it to my object 'sleekResponse'
When I try to parse the body and add it to that object I'm having issues with the return value not including the title that I extracted from the HTML
I know this has something to do with asynchronicity, or my shallow understanding of Promises, but I can't tell why the return value is different from the value it's logging just before it's sent.
async function fetchUrl(url) {
console.log(url);
try {
const myInit = {
mode: 'cors'
}
let sleekResponse = {};
await fetch(url, myInit).then(function (response) {
sleekResponse.redirected = response.redirected;
sleekResponse.status = response.status;
return response;
})
.then((response) => titleWait(response))
.then((res) => sleekResponse.title = res)
function titleWait(response) {
Promise.resolve(response.text()).then((res) => {
a = res.split('<title>');
b = a[1].split('</title>')
sleekResponse.title = b[0];
return sleekResponse;
})
console.log(sleekResponse);
return sleekResponse;
}
console.log(sleekResponse); // This logs the correct value
return sleekResponse; // when it's returned it doesn't show the title that was added
} catch (err) {
return `${err}`;
}
}
I've tried so many things I don't remember everything that I tried. I know that I'm missing something that might be obvious, but I still don't understand why the console.log value is different from the value returned one line later.

The main issue is that titleWait doesn't return its own promise:
function titleWait(response) {
return Promise.resolve(response.text()).then((res) => {
a = res.split('<title>');
b = a[1].split('</title>')
sleekResponse.title = b[0];
return sleekResponse;
});
}
It's still a very convoluted way to write it. This is better:
async function titleWait(response) {
const res = await response.text());
const a = res.split('<title>');
const b = a[1].split('</title>')
sleekResponse.title = b[0];
return sleekResponse;
}

i hope i can a litel help
this basic fetch();
const response = await fetch('your url')
const data = await response.json();
console.log(data);

Related

Promise wont return valid value

I have this test I made just to check an API, but then i tryied to add an URL from a second fetch using as parameter a value obtained in the first fetch and then return a value to add in the first fecth. The idea is to add the image URL to the link. thanks in advance.
function script() {
const url = 'https://pokeapi.co/api/v2/pokemon/?offset=20&limit=20'
const result = fetch(url)
.then( (res)=>{
if(res.ok) {
return res.json()
} else {
console.log("Error!!")
}
}).then( data => {
console.log(data)
const main = document.getElementById('main');
main.innerHTML=`<p><a href='${data.next}'>Next</a></p>`;
for(let i=0; i<data.results.length;i++){
main.innerHTML=main.innerHTML+`<p><a href=${getImageURL(data.results[i].url)}>${data.results[i].name}</a></p>`;
}
})
}
async function getImageURL(imgUrl) {
const resultImg = await fetch(imgUrl)
.then( (res)=> {
return res.json()
})
.then (data => {
console.log(data.sprites.other.dream_world.front_default);
})
return resultImg.sprites.other.dream_world.front_default;
}
In general, don't mix .then/.catch handlers with async/await. There's usually no need, and it can trip you up like this.
The problem is that your fulfillment handler (the .then callback) doesn't return anything, so the promise it creates is fulfilled with undefined.
You could return data, but really just don't use .then/.catch at all:
async function getImageURL(imgUrl) {
const res = await fetch(imgUrl);
if (!res.ok) {
throw new Error(`HTTP error ${res.status}`);
}
const resultImg = await res.json();
return resultImg.sprites.other.dream_world.front_default;
}
[Note I added a check of res.ok. This is (IMHO) a footgun in the fetch API, it doesn't reject its promise on HTTP errors (like 404 or 500), only on network errors. You have to check explicitly for HTTP errors. (I wrote it up on my anemic old blog here.)]
There's also a problem where you use getImageURL:
// Incorrent
for (let i = 0; i < data.results.length; i++) {
main.innerHTML=main.innerHTML+`<p><a href=${getImageURL(data.results[i].url)}>${data.results[i].name}</a></p>`;
}
The problen here is that getImageURL, like all async functions, returns a promise. You're trying to use it as those it returned the fulfillment value you're expecting, but it can't — it doesn't have that value yet.
Instead, you need to wait for the promise(s) youre creating in that loop to be fulfilled. Since that loop is in synchronous code (not an async function), we'd go back to .then/.catch, and since we want to wait for a group of things to finish that can be done in parallel, we'd do that with Promise.all:
// ...
const main = document.getElementById('main');
const html = `<p><a href='${data.next}'>Next</a></p>`;
Promise.all(data.results.map(async ({url, name}) => {
const realUrl = await getImageURL(url);
return `<p><a href=${realUrl}>${name}</a></p>`;
}))
.then(paragraphs => {
html += paragraphs.join("");
main.innerHTML = html;
})
.catch(error => {
// ...handle/report error...
});
For one, your
.then (data => {
console.log(//...
at the end of the promise chain returns undefined. Just remove it, and if you want to console.log it, do console.log(resultImg) in the next statement/next line, after await.
This the final version that accomplish my goal. Just want to leave this just in case someone finds it usefull. Thanks for those who answer!
function script() {
const url = 'https://pokeapi.co/api/v2/pokemon/?offset=20&limit=20'
const result = fetch(url)
.then( (res)=>{
if(res.ok) {
return res.json()
} else {
console.log("Error!!")
}
}).then( data => {
console.log(data)
const main = document.getElementById('main');
main.innerHTML=`<p><a href='${data.next}'>Proxima Página</a></p>`;
Promise.all(data.results.map(async ({url, name}) => {
const realUrl = await getImageURL(url);
return `<div><a href=${realUrl}>${name}</a></div>`;
}))
.then(paragraphs => {
main.innerHTML=main.innerHTML+paragraphs;
})
.catch(error => {
console.log(error);
});
})
}
async function getImageURL(imgUrl) {
const res = await fetch(imgUrl);
if(!res.ok) {
throw new Error(`HTTP Error ${res.status}`)
}
const resultImg = await res.json();
return resultImg.sprites.other.dream_world.front_default
}

Cheerio. Can't get data inside TRY body after handling error of another function

There is a code like this:
const axios = require('axios');
const cheerio = require('cheerio');
let data = null;
const parseNewCorporations = async (iter) => {
let link2 = 'https://www.finanzen.net/aktien/' + iter.finanzen_net + '-aktie';
try{
await axios.get(link2)
.then(res => res.data)
.then(res => {
let html = res;
$ = cheerio.load( html, { decodeEntities: false } );
let bigData = iter;
let price = $('div.snapshot-headline div.col-sm-7 div.row.quotebox:first-child div.col-xs-5.col-sm-4.text-sm-right.text-nowrap').text();
let currency = $('div.snapshot-headline div.col-sm-7 div.row.quotebox:first-child div.col-xs-5.col-sm-4.text-sm-right.text-nowrap span').text();
price = price.replace(currency, '').replace(',', '.');
})
}
catch(e){
console.log(e.message, ', id =', iter.id, ", finanzen_net = "+iter.finanzen_net);
await getAdditionPriceBilanzGuv(iter);
}
};
const getAdditionPriceBilanzGuv = async (iter) => {
//console.log('111', iter); // **here the code works correctly**
let link = 'https://www.finanzen.net/bilanz_guv/'+ iter.finanzen_net;
try{
await axios.get(link)
.then(res => res.data)
.then(res => {
console.log('getAdditionPriceBilanzGuv', iter);
// **here the code works NOT correctly**
})
}
catch(e){
if(e.message == 'Request path contains unescaped characters'){
console.log('Request path contains unescaped characters');
console.log({paramSubLink: iter.finanzen_net, corporations_id: iter.id});
}
else{
console.log('paramCorporationsId: ', iter.id);
//console.log('err- ', e);
}
}
};
function getApiData(){
// get request
return axios.get("https://seo-gmbh.eu/invest/daily.php" , {
})
.then(response => {
return response.data;
})
.catch(function (error) {
console.log(error);
});
}
async function new_corporations() {
data = await getApiData();
let ii = 1;
for (let iter of data.new_corporations) {
//await parseNewCorporations(iter);
ii++;
await setTimeout(function(){
parseNewCorporations(iter);
}, ii*3000);
}
//console.log(arrayCurrency);
}
new_corporations();
After calling the parseNewCorporations () function, the catch () exception is triggered as a result of which
the corresponding messages can be seen in the console.
The problem is that when this error appears, you need to run the following function getAdditionPriceBilanzGuv () with the iter parameter, and inside the body oftry {}you need to get this parameter, which cannot be done.
At the very beginning (outside the body of try {}) of this function (where it is indicated by a comment that the code works), it is possible to get this parameter.
Question:
What am I missing and how can I get this parameter in a newly called function inside the body of try {}?
If it is impossible to do this, what alternative implementations can be for solving this problem?
P.S. In this case, a parsing library is used cheerio
If you use async / await, you don't want thens
let res = await axios.get(link2)
let $ = cheerio.load(res.data)

I get Promise { <pending> } as returned value and also calling in async scope gives me undefined immediately

Im trying to return a value from a Promise in async-await form and use it in another function in another file, but I do have problem because my Promise doesnt return any value.
When im trying to console.log('website') it returns me undefined immediately (it's like the value is not being fetched at all from API services). I dont know what im doing wrong, I really love to learn about Promises and Async-Await but each time im trying to work with them im getting more confused.
const dns = require('dns')
const iplocation = require("iplocation").default;
const emojiFlags = require('emoji-flags');
const getServerIPAddress = async (server) => {
return new Promise((resolve, reject) => {
dns.lookup(server, (err, address) => {
if (err) throw reject(err);
resolve(address);
});
});
};
const getServerLocation = async (server) => {
const ip = await getServerIPAddress(server)
iplocation(ip).then((res) => {
const country = emojiFlags.countryCode(res.countryCode)
const result = `Location: ${country.emoji} ${country.name}`
return result
})
.catch(err => {
return `Location: Unknown`
});
}
(async function() {
console.log(await getServerLocation('www.google.com'))
})()
module.exports = {
getServerLocation
}
It is really important for me to get result from this function first, then use its value in another function. I wish you could give me tips on how to do tasks asynchronously.
You're clearly using async so it's not apparent why you're using then as well. If you use then then you must return the promise as well in order to preserve the promise chain:
const getServerLocation = async (server) => {
const ip = await getServerIPAddress(server)
return iplocation(ip).then((res) => {
const country = emojiFlags.countryCode(res.countryCode)
const result = `Location: ${country.emoji} ${country.name}`
return result
})
.catch(err => {
return `Location: Unknown`
});
}
Otherwise just async this:
const getServerLocation = async (server) => {
const ip = await getServerIPAddress(server)
let res = await iplocation(ip);
const country = emojiFlags.countryCode(res.countryCode)
const result = `Location: ${country.emoji} ${country.name}`
return result
}
const getServerLocation = async (server) => {
const ip = await getServerIPAddress(server)
//you need to return
return iplocation(ip).then((res) => {
const country = emojiFlags.countryCode(res.countryCode)
const result = `Location: ${country.emoji} ${country.name}`
return result
})
.catch(err => {
return `Location: Unknown`
});
}

"Exit promise" with multiples fetch requests

I need to merge data from API. I do a first call to an endpoint that gives me a list of ids, then I do a request for each id. My goal is to return a list with the responses of all requests but I lost myself in promises ...
My code runs on NodeJS. Here is the code :
const fetch = require('node-fetch')
const main = (req, res) => {
fetch('ENDPOINT_THAT_GIVES_LIST_OF_IDS')
.then(response => response.json())
.then(response => {
parseIds(response)
.then(data => {
console.log(data)
res.json(data)
// I want data contains the list of responses
})
})
.catch(error => console.error(error))
}
const getAdditionalInformations = async function(id) {
let response = await fetch('CUSTOM_URL&q='+id, {
method: 'GET',
});
response = await response.json();
return response
}
const parseIds = (async raw_ids=> {
let ids= []
raw_ids.forEach(function(raw_id) {
let informations = {
// Object with data from the first request
}
let additionalInformations = await
getAdditionalInformations(raw_id['id'])
let merged = {...informations, ...additionalInformations}
ids.push(merged)
})
return ids
})
main()
I get this error : "await is only valid in async function" for this line :
let additionalInformations = await getAdditionalInformations(raw_id['id'])
Help me with promise and async/await please.
You're almost there, just a slight bit of error here with your parentheses:
// notice the parentheses'
const parseIds = async (raw_ids) => {
let ids= []
raw_ids.forEach(function(raw_id) {
let informations = {
// Object with data from the first request
}
let additionalInformations = await getAdditionalInformations(raw_id['id'])
let merged = {...informations, ...additionalInformations}
ids.push(merged)
})
return ids
}
You are missing an async after forEach
const parseIds = (async raw_ids=> {
let ids= []
raw_ids.forEach(async function(raw_id) {
let informations = {
// Object with data from the first request
}
let additionalInformations = await
getAdditionalInformations(raw_id['id'])
let merged = {...informations, ...additionalInformations}
ids.push(merged)
})
return ids
})
One suggestion: you are mixing promises (.then()) with async/await. Prefer async/await is more readable.
Note that getAdditionalInformations inside forEach doesn't wait for it to be done before going to the next entry of the array.
You can use plain old for(var i=0; .... instead

Using promises in Axios requests

I am trying to work out the best way to achieve something. When I land on a Profile page, the Profile component loads the data for that profile. This is assigned to this.profile. Within this data is a path to a file, where I want to process some data using this file. To me, the below approach seems slightly risky.
created() {
let vm = this;
let url = `/api/profile/${this.$route.params.id}`;
axios.get(url).then(response => {
this.profile = response.data;
d3.json(response.data.fileName)
.then(function (data) {
//do some stuff
}).catch(function (error) {
// handle error
});
});
}
Instead of that, I want to ensure that I first have the data from the axios call. So I am thinking I need a promise? I was thinking something more along the lines off
created() {
let vm = this;
let url = `/api/profile/${this.$route.params.id}`;
axios.get(url).then(response => {
this.profile = response.data;
}).then() {
d3.json(response.data.fileName)
.then(function (data) {
//do some stuff
}).catch(function (error) {
// handle error
});
};
}
But the above is incorrect, it is mainly to show what I am trying to achieve. I was wondering how I can maybe use deferred and promises to only execute the d3 stuff once the axios call is made.
Thanks
You can solve this by chaining promises, assuming that d3.json returns a promise:
created() {
let vm = this;
let url = `/api/profile/${this.$route.params.id}`;
axios.get(url)
.then(response => {
this.profile = response.data
return d3.json(response.data.fileName)
}).then(data => {
//do some stuff
}).catch(err => {
//log error
})
}
That's where async/await comes in handy. A you don't need to save this to a variable and B you have cleaner, more readable code.
async created() {
const url = `/api/profile/${this.$route.params.id}`;
const { data } = await axios.get(url); // Optional destructuring for less clutter
this.profile = data;
const d3Data = await d3.json(data.fileName);
//do whatever you want
}
async created() {
let vm = this;
let url = `/api/profile/${this.$route.params.id}`;
try {
const {data} = await axios.get(url)
const d3Data = await d3.json(data.fileName)
} catch(err) {
//error
}
}

Categories