How do I make the output of this Javascript API response global? - javascript

I'm trying to receive the output for the following code where the cc variable would log a value into the empty global country variable. Afterwards print it to the console, however it isn't working. How would I make the local variable cc here to global/give the global variable country a value?
var country = '';
fetch('https://extreme-ip-lookup.com/json/')
.then( res => res.json())
.then(response => {
var cc = (response.countryCode);
country = cc;
});
console.log(country);

It seems like your problem has to do with the asynchronous nature of your code. Let me explain.
var country = '';
fetch('https://extreme-ip-lookup.com/json/')
.then( res => res.json())
.then(response => {
var cc = (response.countryCode);
country = cc;
});
console.log(country);
The fetch function is asynchronous. That's why you need the .then methods. This means that while the fetch function runs, JavaScript doesn't stop the rest of the program, and instead moves on while fetch() runs in the background. Hence, when you console.log(country), it's still of the original value (empty string).
To answer your question, you can use Promises to log the value of cc.
var country = '';
const fetchPromise = fetch('https://extreme-ip-lookup.com/json/')
.then( res => res.json())
.then(response => {
var cc = (response.countryCode);
country = cc;
});
Promise.resolve(fetchPromise) // Waits for fetchPromise to get its value
.then(() => console.log(country))
You can find out more about promises at the MDN docs

The problem with your currently call console.log(country) before country is set to response.countryCode.
You could solve this by placing your code inside an async IIFE in the following manner:
(async () => {
const response = await fetch('https://extreme-ip-lookup.com/json/');
const ipData = await response.json();
const country = ipData.countryCode;
// place all code that uses `country` in here
console.log(country);
})();
If you have another script with function definitions that depends on county be sure to accept it as parameter and don't pull the data from a global variable.
// helper_functions.js
// Bad
function someFunctionThatUsesCountry() {
console.log(country); // <- don't expect the global to be set
}
// Good
function someFunctionThatUsesCountry(country) {
console.log(country); // pull country ^ from the parameter list
}
You can then call you other script inside the IIFE by just passing the value.
(async () => {
// ...
someFunctionThatUsesCountry(country);
})();
If for some reason want a global variable really bad. You should place the promise inside this variable, not the value. With this promise you can pass the value, as well as notify other scripts when this value is available.
// script_1.js
window.country = fetch('https://extreme-ip-lookup.com/json/')
.then(response => response.json())
.then(ipData => ipData.countryCode);
// script_2.js (must be loaded after script_1.js)
window.country.then(country => { // <- wait until country is available
// do stuff with country
console.log(country);
});

Your problem come from the fact that your fetch is an asynchrone function, with a promise.
What you want to do is that (I suppose)
var country = '';
//then
fetch('https://extreme-ip-lookup.com/json/')
.then( res => res.json())
.then(response => {
var cc = (response.countryCode);
country = cc;
});
//then
console.log(country);
But, since you use an async function, this is what is done:
//first
var country = '';
//second
fetch('https://extreme-ip-lookup.com/json/')
.then( res => res.json())
.then(response => {
//fourth
var cc = (response.countryCode);
country = cc;
});
//third
console.log(country);
How to fix that? it depend. If your console.log is triggered by a button, make it wait for country to be filled
else, put your code in the last then, or use Promise.all() (documentation here)

console.log is happening before fetch is resolved. Try this:
let country = '';
fetch('https://extreme-ip-lookup.com/json/')
.then(res => res.json())
.then(res => country = res.countryCode)
.then(() => console.log(country))

fetch returns a Promise, hence the .then() chain in your question.
But if you stepped into Promise Land, you won't find out. You'll have to do everything with Promises.
But you might split you logic into small manageable parts like here
console.clear()
let cached = null;
const lookup = () => fetch('https://extreme-ip-lookup.com/json/');
// Caching json and not the fetch promise, because
// https://stackoverflow.com/a/54731385/476951
const getJson = () => cached = cached || lookup() .then(response => response.json());
const getParameter = (parameter) => getJson() .then(json => json[parameter]);
const getCountry = () => getParameter('country') .then(value => value);
const getCountryCode = () => getParameter('countryCode') .then(value => value);
getCountryCode().then(countryCode => {
console.log('countryCode:', countryCode)
})
getCountry().then(country => {
console.log('country:', country)
})
getParameter('city').then(city => {
console.log('city:', city)
})

Related

Prevent same call on an api response

I'm learning to use calls on APIs and I'm trying to get my API response to not return the same data over and over again, currently, it does despite using setInterval, I have tried changing the order and making it async but am currently not able to figure out how to change this.
The idea is to make an original call to the API on page load, then after 6 or so seconds, make the call again but change the response automatically hence the setInterval.
Here is my code:
const advice = document.getElementById("advice");
const adviceNum = document.getElementById("adviceNum");
const slip_id = Math.random() * 100;
console.log(slip_id)
fetch(`https://api.adviceslip.com/advice/${slip_id}`)
.then(response => {
return response.json();
})
.catch(error => {
console.log(error);
})
.then(data => {
console.log(data);
const returnedAdvice = data.slip.advice;
console.log(returnedAdvice);
const idAdvice = data.slip.id;
adviceNum.innerText = `ADVICE #${idAdvice}`;
advice.innerText = `"${returnedAdvice}"`;
setInterval(() => {
console.log(data);
const returnedAdvice = data.slip.advice;
console.log(returnedAdvice);
const idAdvice = data.slip.id;
adviceNum.innerText = `ADVICE #${idAdvice}`;
advice.innerText = `"${returnedAdvice}"`;
}, 8000)
})
Would appreciate any help on what I'm doing wrong here!
Currently you're making a fetch request and when the data returns from that single request; you console.log etc.
Instead try the following, wrap your fetch request in the setInterval
setInterval(async function(){
const data = await fetch(`https://api.adviceslip.com/advice/${slip_id}`);
// check your response
// compute your values
// add them to the dom
}, 8000);

Changing template literals variable's value inside JavaScript

I have been making a pokedex web app just for fun, but i'm struggling to change the pokemon's atributes so that the user can see multiple pokemon. I'm using the PokeAPI and made the url change with a template literal:
let pokeNumber = 1
let url = `https://pokeapi.co/api/v2/pokemon/${pokeNumber}
console.log(url)
function getPokemon() {
fetch(url)
.then((response) => {
return response.json();
})
.then((data) => {
console.clear()
console.log(data)
document.querySelector('.poke_name').innerHTML = data['name'].toUpperCase()
document.querySelector('.poke__peso').innerHTML = `Peso: ${data['weight']}kg`
document.querySelector('.poke__altura').innerHTML = `Altura: ${data['height']}m`
let img = data['sprites']['front_default']
document.querySelector('.poke_img').setAttribute('src', img)
})
}
getPokemon()
document.querySelector('.btn_forw').onclick = () => {
console.log(url)
pokeNumber++;
console.log(pokeNumber)
}
document.querySelector('.btn_backw').onclick = () => {
console.log(url)
pokeNumber--;
console.log(pokeNumber)
}
The variables inside a template string are not stored by reference, instead they are populated immediately when declaring that variable.
For example
let userId = 1;
let url = `/user/${userId}`
console.log(url); // "/user/1"
// we're only changing the `userId` variable - not the `url` variable
userId = 2;
console.log(url); // "/user/1"
// we can then re-declare the `url` variable the same way
url = `/user/${userId}`
console.log(url); // "/user/2"
So generally what you need to do is re-declare the url variable each time you change any variable that it used to construct that string.
Alternatively, you could put it in a function and call that each time, e.g.
function getUrl(){
return `https://pokeapi.co/api/v2/pokemon/${pokeNumber}`
}
// or ES6 one liner
const getUrl = () => `https://pokeapi.co/api/v2/pokemon/${pokeNumber}`
// then instead of using `url`, replace it with the function call `getUrl()`
function getPokemon() {
fetch(getUrl())
.then((response) => {
return response.json();
})
}
// alternatively, if you are not using the `url`
// variable outside the `getPokemon` function,
// you could re-define that within it:
function getPokemon() {
let url = `https://pokeapi.co/api/v2/pokemon/${pokeNumber}`;
fetch(url)
.then((response) => {
return response.json();
})
}

How to find a word count and collect word and pass on url javascript

I have fetched a text doc from an url and stored in the response.
I want to do 3 things here:-
Find count of words from the doc.
Collect details for top 3 words (order by word occurrences) and show synonyms and part of speech.
API :- https://dictionary.yandex.net/api/v1/dicservice.json/lookup Key :- something..
Show above top 3 word list results in json format
All should be in asynchronous way.
const fetch = require("node-fetch");
async function fetchTest() {
let response = await
fetch('http://norvig.com/big.txt')
.then(response => response.text())
.then((response) => {
console.log(response)
})
.catch(err => console.log(err));
}
(async() => {
await fetchTest();
})();
EDIT:-
const fetch = require("node-fetch");
async function fetchTest(responses) {
let response = await
fetch('http://norvig.com/big.txt')
.then(response => response.text())
.then((response) => {
console.log(response); // ------------------> read text from doc
var words = response.split(/[ \.\?!,\*'"]+/);
console.log(words);
var array = Object.keys(words).map(function(key) {
return { text: words[key], size: key };
});
console.log(array); //-----------------------> occurence count of words
var sortedKeys = array.sort(function (a, b) {
return b.size - a.size ;
});
var newarr = sortedKeys.slice(0, 10);
console.log(newarr); // --------------> top 10 text and occurence
var permittedValues = newarr.map(value => value.text);
console.log(permittedValues); //---------------> sorted key in array
fetch('https://dictionary.yandex.net/api/v1/dicservice.json/lookup?key=domething_&lang=en-en&text=in&callback=myCallback')
.then(responses => responses.text())
.catch(err => console.error(err));
console.log(this.responses);
})
.catch(err => console.log(err));
}
(async() => {
await fetchTest();
})();
Unable to do fetch API with json response to show synonyms and part of speech. Where am I going wrong ?
This sounds a lot like a homework question. If you look at the documentation for the lookup call:
https://tech.yandex.com/dictionary/doc/dg/reference/lookup-docpage/
it offers both an array of synonyms and the part of speech for free. which means you only need to determine the most used words.
It may not be the flashiest solution, but you can solve this in short order using a dictionary. Iterate once storing a count in a dictionary then order the key/value pairs by value and you have the solution. You'll have the top ten words and you have an api to provide the rest, with your current code showing how to async call the api.
Seems straight forward from there.

Call Fetch Data from API once

Fetch is often used to retrieve data from api. But if you want to retrieve this data in every line of the code file, you have to type fetch again and again and iterate through these data. But if i want to call only part of it for example username. Look example:
fetch ('/ API')
         .then ((res) => res.json ())
         .then ((data) => {
         let output = 'Here is my output<br />';
         data.forEach (function (item)
         {
         output + = `$ {item.username}`;
         })
         document.getElementById ('here'). innerHTML = output;
         })
if i want to call $ {item.username} in each place in the file without calling fetch.
How to save these data in a variabel and call it every time i want.
Sorry for my bad English.
But if you want to retrieve this data in every line of the code file, you have to type fetch again and again and iterate through these data.
You can call fetch one time and keep the response in your local state, things like username usually stay the same so you should not call that endpoint again just to fetch a username because you can take it from your previous response.
It depends on your framework, but you can write window.username = usernameFromFetch on your first call, and you will have access to it every where (which is not the best aproach but it will work).
From what I understand through your question is that you want to access variable item.username globally and from the code it looks to be an array of users with username property.
In this case, you can define a variable object
var data = {
usernames = []
};
before your fetch function and the value will be initialized to an empty array and as soon as you fetch('/API') users you can do something like this
data.forEach (function (item)
{
data.usernames.push(item.username);
})
data.usernames will have your usernames throughout your current JS file.
access by
data.usernames[0]
data.usernames[1]
data.usernames[2] or
let output = 'Here is my output<br />';
data.usernames.forEach(function(username){
output + = `$ {username}`;
})
document.getElementById('here').innerHTML = output;`
You can use a memoization function to save the result:
// Stores in memory pairs of request urls (arguments) and its result
const memo = (callback) => {
const cache = new Map();
return (...args) => {
const selector = JSON.stringify(args);
if (cache.has(selector)) return cache.get(selector);
const value = callback(...args);
cache.set(selector, value);
return value;
};
};
// Memoize the fetch function. This function 'cachedRequest' should be used across your application
const cachedRequest = memo(fetch);
// Your url to call
const URL = "/API";
// First time the fetch is called, the result will be stored in the memory of 'memo' function. Second time its called, it will be retrieved from the previous in memory result and the fetch call wont be done
cachedRequest(URL).then((response) => {
console.log({response});
}).catch((error) => {
console.log({error});
})
Try this using localStorage:
fetch ('/ API')
.then ((res) => res.json ())
.then ((data) => {
// Store
localStorage.setItem("localData", data);
// Retrieve
var data = localStorage.getItem("localData");
let output = 'Here is my output<br />';
data.forEach (function (item)
{
output + = `$ {item.username}`;
})
document.getElementById ('here'). innerHTML = output;
})
Or use sessionStorage:
fetch ('/ API')
.then ((res) => res.json ())
.then ((data) => {
// Store
sessionStorage.setItem("localData", data);
// Retrieve
var data = sessionStorage.getItem("localData");
let output = 'Here is my output<br />';
data.forEach (function (item)
{
output + = `$ {item.username}`;
})
document.getElementById ('here'). innerHTML = output;
})

Getting Text From Fetch Response Object

I'm using fetch to make API calls and everything works but in this particular instance I'm running into an issue because the API simply returns a string -- not an object.
Typically, the API returns an object and I can parse the JSON object and get what I want but in this case, I'm having trouble finding the text I'm getting from the API in the response object.
Here's what the response object looks like.
I thought I'd find the text inside the body but I can't seem to find it. Where do I look?
Using the fetch JavaScript API you can try:
response.text().then(function (text) {
// do something with the text response
});
Also take a look at the docs on fetch > response > body interface methods
ES6 Syntax:
fetch("URL")
.then(response => response.text())
.then((response) => {
console.log(response)
})
.catch(err => console.log(err))
You can do this in two different ways:
The first option is to use the response.text() method, but be aware that, at Dec/2019, its global usage is only 36.71%:
async function fetchTest() {
let response = await fetch('https://httpbin.org/encoding/utf8');
let responseText = await response.text();
document.getElementById('result').innerHTML = responseText;
}
(async() => {
await fetchTest();
})();
<div id="result"></div>
The second option is to use the response.body property instead, which requires a little more work but has 73.94% of global usage:
async function fetchTest() {
let response = await fetch('https://httpbin.org/encoding/utf8');
let responseText = await getTextFromStream(response.body);
document.getElementById('result').innerHTML = responseText;
}
async function getTextFromStream(readableStream) {
let reader = readableStream.getReader();
let utf8Decoder = new TextDecoder();
let nextChunk;
let resultStr = '';
while (!(nextChunk = await reader.read()).done) {
let partialData = nextChunk.value;
resultStr += utf8Decoder.decode(partialData);
}
return resultStr;
}
(async() => {
await fetchTest();
})();
<div id="result"></div>

Categories

Resources