Assign return value of function to variable - javascript

I kind of got the same question as has been asked here.
I've made a function:
const rewardStayingViewersOrNewcomers = () => {
fetch('https://tmi.twitch.tv/group/user/instak/chatters')
.then(parseJSON)
.then(r => {
let chatters = r.chatters;
viewerKeys = Object.keys(chatters); // [mods, viewers,...]
let ChattersPerRole = viewerKeys.map(role => {
return chatters[role].map(username => ({
username, role
}));
});
return flattenDeep(ChattersPerRole);
}).catch(err => {
console.log(`Error in fetch: ${err}`);
});
};
Why can't I assign that return value to my variable? The log of that variable returns undefined...
let viewersPerRole = rewardStayingViewersOrNewcomers();
setTimeout(() => console.log(viewersPerRole), 7000);
Bonus question, how could I easily wait for viewersPerRole to be filled with the data I'm waiting for because of the fetch? (so I don't have to use setTimeout())?

First of all, try returning something from the main function. There's no return in front on the fetch(...). Code should look like:
const rewardStayingViewersOrNewcomers = () => {
return fetch('https://tmi.twitch.tv/group/user/instak/chatters')
.then(parseJSON)
.then(r => {
let chatters = r.chatters;
viewerKeys = Object.keys(chatters); // [mods, viewers,...]
let ChattersPerRole = viewerKeys.map(role => {
return chatters[role].map(username => ({
username, role
}));
});
return Promise.resolve(flattenDeep(ChattersPerRole));
}).catch(err => {
console.log(`Error in fetch: ${err}`);
});
};
// now that you're returning a promise
rewardStayingViewersOrNewcomers()
.then(viewersPerRole => console.log(viewersPerRole))
If you're using Babeljs to transpile with stage3 enabled, have a look at async / await.

Related

Can't iterate over my API generated array using the forEach() method

After troubleshooting with console.log/debugger it seems like I cannot iterate over my API generated array at the forEach method call in the function addListItem.
However I can see the pokemonNameList array being populated in the forEach iteration in the loadList function.
What am I doing wrong?
const apiUrl = 'https://pokeapi.co/api/v2/pokemon/?limit=15';
const pokemonNameList = [];
function getAll() {
return pokemonNameList;
}
function add(pokemon) {
if (typeof pokemon === 'object') {
pokemonNameList.push(pokemon);
}
}
function loadList() {
return fetch(apiUrl)
.then((response) => response.json())
.then((data) => {
data.results.forEach((item) => {
fetch(item.url)
.then((response) => response.json())
.then((inneritem) => {
const pokemon = {
name: inneritem.name,
height: inneritem.height,
weight: inneritem.weight
};
add(pokemon);
console.log(pokemonNameList);// I can see the array here
});
});
})
.then(() => {
console.log(pokemonNameList);
})
.catch((e) => {
console.error(e);
});
}
function addListItem(pokemon) {
console.log('I cannot see this console log');//This does not show up
const card = document.createElement('li');
const cardbody = document.createElement('div');
const name = document.createElement('h1');
card.classList.add('card');
cardbody.classList.add('card-body');
name.classList.add('card-title');
name.innerText = pokemon.name;
cardbody.appendChild(name);
card.appendChild(cardbody);
pokemonList.appendChild(card);
}
loadList()
.then(() => {
getAll().forEach((item) => {
console.log('Hello from inside the forEach');//I cannot see this
addListItem(item);
});
})
.catch((e) => {
console.error(e);
});
The problem is that you are not waiting for the inner fetch(item.url)s so when you call getAll no item has been pushed yet.
you can do that by changing forEach to map, returning the promise and adding a promise.all... something like this:
function loadList() {
return fetch(apiUrl)
.then((response) => response.json())
.then((data) => {
return Promise.all(data.results.map((item) => {
return fetch(item.url)
...
I created all the functions up to the place where you mentioned the error
const pokemonNameList = []; // Pokemon Array
const apiUrl = 'https://pokeapi.co/api/v2/pokemon/?limit=15'; // API URL
// To prevent duplicates, in case of calling the loadList function multiple times, i'm passing the index from the response, to replace the element at the same index
const add = (pokemon, index) => pokemonNameList[index] = (pokemon);
const getAll = _ => pokemonNameList; // Short arrow function to return pokemonNameList
async function loadList() {
const response = await fetch('https://pokeapi.co/api/v2/pokemon/?limit=5');
const result_1 = await response.json();
Promise.all(result_1.results.map((item, index) => fetch(item.url).then(response_1 => response_1.json()).then(({
name,
height,
weight
}) => add({
name,
height,
weight
}, index)))).then(() => getAll().forEach(pokemon => console.log(pokemon)));
}

How can I use a for loop to reiterate a promise function?

I have the following code that is used to get JSON data from an Amazon Web Server API.
var json1 = new Promise((resolve, reject) => {
fetch(url[0])
.then(r => {
resolve(r.json())
})
.catch(err => {
reject(err)
})
})
I have this repeating 14 times using different urls and json vars and have it return the promises at the end using.
return Promise.all([json1,json2,json3,json4,json5,json6,json7,json8,json9,json10,json11,json12,json13,json14]).then(function(values) {
return values;
});
This works, but it takes up 150+ lines. I want to make a for loop that runs through the same code using a for loop. I created this...
for(var jsonCount = 0;jsonCount<url.length-1;jsonCount++){
jsonArr[jsonCount] = new Promise((resolve, reject) => {
fetch(url[jsonCount])
.then(r => {
resolve(r.json())
})
.catch(err => {
reject(err)
})
})
}
This doesn't work because the promise functions come back as undefined even though it is called by an await function.
const data = await fetchURL(urlToQuery())
Does anyone have suggestions to make this work? There is JSON being returned.
Thanks for your help.
Edit: Here is a larger chunk of the code.
function fetchURL(urls) {
let fetchJson = url => fetch(url).then(response => response.json());
Promise.all(urls.map(fetchJson)).then(arr => {
return arr;
});
(async function() {
const data = await fetchURL(urlToQuery())
console.log(data);
for(var r=0;r<numStations;r++){
if (data[r] == ""){
onlineArr[r] = false;
wdDataArr[r].push(cardinalToDeg(stationHistAvgArr[r]));
wsDataArr[r].push(0);
You can use .map for the loop. But don't use new Promise. You don't need a new promise when fetch already provides you with one.
Also, call your array urls instead of url. A plural will be a good indication for the reader of your code that indeed it is a collection of URLs.
Here is how it could look:
let fetchJson = url => fetch(url).then(response => response.json());
Promise.all(urls.map(fetchJson)).then(arr => {
// process your data
for (let obj of arr) {
console.log(obj);
}
});
I think this example can helps you:
// Mock async function
const getDataAsync = callback => {
setTimeout(
() => callback(Math.ceil(Math.random() * 100)),
Math.random() * 1000 + 2000
)
}
// Create the promise
const getDataWithPromise = () => {
return new Promise((resolve, reject) => {
try {
getDataAsync(resolve);
} catch(e) {
reject(e);
}
});
}
// Using the promise one time
getDataWithPromise()
.then(data => console.log("Simple promise:",data))
.catch(error => console.error(`Error catched ${error}`));
// Promises compound: Promise.all
const promise1 = getDataWithPromise();
promise1.then(data => console.log("promise1 ends:",data));
const promise2 = getDataWithPromise();
promise2.then(data => console.log("promise2 ends:",data));
const promise3 = getDataWithPromise();
promise3.then(data => console.log("promise3 ends:",data));
const promise4 = getDataWithPromise();
promise4.then(data => console.log("promise4 ends:",data));
const promise5 = getDataWithPromise();
promise5.then(data => console.log("promise5 ends:",data));
Promise.all([promise1,promise2,promise3,promise4,promise5])
.then(data => console.log("Promise all ends !!",data));
Hope this helps
you will have issues with closure and var variable capture.
You may want to change var to let to capture the right value in the closure so that url[jsonCount] is actually what you want.
also I think it would be much easier to do something like that in one line :)
let results = [];
for(let i = 0; i < urls.length; ++i) results.push(await (await fetch[urls[i]]).json());
This is a good use for map, mapping urls to promises...
function fetchUrls(urls) {
let promises = urls.map(url => fetch(url))
return Promise.all(promises).then(results => {
return results.map(result => result.json())
})
}}
// url is your array of urls (which would be better named as a plural)
fetchUrls(url).then(results => {
// results will be the fetched json
})
Using the async/await syntax (equivalent meaning)
// this can be called with await from within another async function
async function fetchUrls(urls) {
let promises = urls.map(url => fetch(url))
let results = await Promise.all(promises)
return results.map(result => result.json())
}

Uncaught TypeError: Cannot read property 'then' of undefined at HTMLFormElement.<anonymous>

All code work good, but when I put then to the returned module all code crashes and throws error. Is the problem from that the export is function? If it is not from the function may someone explain why?
This is the module
export default {
search: function(searchTerm, searchLimit, sortBy) {
fetch(
`http://www.reddit.com/search.json?q=${searchTerm}&sort=${sortBy}&limit=${searchLimit}`
)
.then(res => res.json())
.then(data => data.data.children.map(data => data.data))
.catch(err => console.log(err));
}
};
This is actual main JavaScript file
import reddit from "./redditApi";
const searchForm = document.querySelector("#search-form");
const searchInput = document.querySelector("#search-input");
// form eventlistener
searchForm.addEventListener("submit", e => {
e.preventDefault();
// get search term
const searchTerm = searchInput.value;
// get sort
const sortBy = document.querySelector('input[name="sortby"]:checked').value;
// get limit
const searchLimit = document.querySelector("#limit").value;
// check input
if (searchTerm === "") {
// show message
showMessage("Please add a search Term!", "alert-danger");
}
// clear input
searchInput.value = "";
// search reddit
reddit.search(searchTerm, searchLimit, sortBy).then(results => {
console.log(results);
});
});
fetch returns a promise, but search doesn't have any return statement at all.
After you call catch on it, the promise is discarded.
If you want to use it outside the search function then you need to return it.
If you intend to do that
reddit.search(searchTerm, searchLimit, sortBy)
.then(results => {
console.log(results);
})
.catch(error => {
console.log(error);
});
you should wrap fetch inside a Promise.
example:
export default {
search: (searchTerm, searchLimit, sortBy) => {
return new Promise((resolve, reject) => {
fetch(`http://www.reddit.com/search.json?q=${searchTerm}&sort=${sortBy}&limit=${searchLimit}`)
.then(res => res.json())
.then(data => resolve(data.data.children.map(data => data.data)))
.catch(err => reject(err));
});
}
};

Javascript - Chain multiple Fetch promises

I have this method who performs 3 window.fetch
const API_URL = 'http://localhost:3000/'
, API = {
'getArtistLyric': artist => {
return fetch(`${API_URL}artist?name=${artist}`)
.then(res => res.json())
.then(res => {
const artistID = JSON.parse(res).message.body.artist_list[0].artist.artist_id;
console.log('Artist ID is:', artistID);
fetch(`${API_URL}artist/albums/?artist_id=${artistID}`)
.then(resp => resp.json())
.then(resp => {
const trackID = JSON.parse(resp).message.body.album_list[0].album.album_id;
console.log('Random Track ID is:', trackID);
fetch(`${API_URL}artist/album/songsnippet?track_id=${trackID}`)
.then(response => response.json())
.then(response => {
const lyricSnippet = JSON.parse(response).message;
console.log('Track Id lyric snippet is:', lyricSnippet);
})
.catch(err => {
console.error(err);
});
})
.catch(err => {
console.error(err);
});
})
.catch(err => {
console.error(err);
});
}
}
Now i want to call it like this
API.getArtistLyric('Prodigy').then(res).catch(err);
What's the best practice here?
If you want to make a chain requests it's better to use async/await :
async func(){
let response = await /* some request */
let res = await /* another request */
...
return results;
}
Here you can use try/catch syntax and wrap specific request :
try {
let response = await...
} catch ( exception) {
...
}
Also you can wrap a couple of requests.
(async() => {
const API_URL = 'http://localhost:3000/';
const API = {
getArtistLyric: async(artist) => {
try {
const res = await fetch(`${API_URL}artist?name=${artist}`);
const artistID = JSON.parse(res.json()).message.body.artist_list[0].artist.artist_id;
console.log('Artist ID is:', artistID);
const resp = await fetch(`${API_URL}artist/albums/?artist_id=${artistID}`);
const trackID = JSON.parse(resp.json()).message.body.album_list[0].album.album_id;
console.log('Random Track ID is:', trackID);
const response = await fetch(`${API_URL}artist/album/songsnippet?track_id=${trackID}`);
const lyricSnippet = JSON.parse(response.json()).message;
console.log('Track Id lyric snippet is:', lyricSnippet);
return lyricSnippet;
} catch (e) {
console.error(e);
}
}
}
try {
const art = await API.getArtistLyric('Prodigy');
console.log(art);
} catch (e ){
console.error(e);
}
})()
Check out this link regarding chaining promises: https://javascript.info/promise-chaining
Here's the core idea:
It looks like this:
new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000); // (*)
}).then(function(result) { // (**)
alert(result); // 1 return result * 2;
}).then(function(result) { // (***)
alert(result); // 2 return result * 2;
}).then(function(result) {
alert(result); // 4 return result * 2;
});
The idea is that the result is passed through the chain of .then
handlers.
Here the flow is:
The initial promise resolves in 1 second (*), Then the .then handler
is called (**). The value that
it returns is passed to the next .then handler (***) …and so on.

How to return a single promise after for loop where every element of an array inside loop do multi async calls

I Have a array of objects which i need to clone with different values.
Those values i'll get from each promise finally after preparing main modified array of object i'll have to save this. So i many need this as one single promise.
I not sure how to do it.
Here is the example we need to clone oldUser data. Say old user has credit score = 100; but for new user default credit will be created randomly by system.
For each user in the array of users few details has to get updated using async call.
This is the requirement
function getUserCreditScore(user){
var url = '/someurl';
return $http.get(url).then(function(res){
user.creditScore = (res.data) ? res.data : 0;
});
}
function getUserRecomandations(user){
var url = '/someurl';
return $http.get(url).then(function(res){
user.recommendation = (res.data) ? res.data : 'basic recommendation';
});
}
function getUserHelpInfo(user){
var url = '/someurl';
return $http.get(url).then(function(res){
user.helpInfo = (res.data) ? res.data : 'Help Info';
});
}
function clone(){
var newUsers = angular.copy(oldUsers);
for (var i=0; i<newUsers.length; i++){
newUsers[i].id = undefined;
getUserCreditScore(newUsers[i]);
getUserRecommendation(newUsers[i]);
getUserHelpInfo(newUsers[i]);
}
var promises = _.map(newUsers, user => user.save());
$q.all(promises).then(function (data) {
console.log(data);
}
}
You'll need to Promise.all on an array of Promises that are returned by getScreditScore
something like
function getCreditScore(){
var url = '/someurl';
return $http.get(url).then(res => (res && res.data) ? res.data : res);
}
function clone(){
var newUsers = angular.copy(oldUsers);
Promise.all(
newUsers.map(newUser => {
newUser.id = undefined;
return getCreditScore()
.then(result => newUser.creditScore = result);
})
).then(results => // results will be an array of values returned by the get in getCreditScore(newUser)
Promise.all(newUsers.map(user => user.save()))
).then(data =>
console.log(data); // this will be the result of all the user.save
);
}
Note: the newUser.creditScore is set in the .then in the newUsers.map callback - (minimal change to my original answer)
Alternatively, passing user to getCreditScore
function getCreditScore(user){
var url = '/someurl';
return $http.get(url)
.then(res => (res && res.data) ? res.data : res)
.then(score => user.creditScore = score);
}
function clone(){
var newUsers = angular.copy(oldUsers);
Promise.all(
newUsers.map(newUser => {
newUser.id = undefined;
return getCreditScore(newUser);
})
).then(results => // results will be an array of values returned by the get in getCreditScore(newUser)
Promise.all(newUsers.map(user => user.save()))
).then(data =>
console.log(data); // this will be the result of all the user.save
);
}
Personally, I'd write the code
function getCreditScore(){
var url = '/someurl';
return $http.get(url).then(res => (res && res.data) ? res.data : res);
}
function clone(){
var newUsers = angular.copy(oldUsers);
Promise.all(
newUsers.map(newUser => {
newUser.id = undefined;
return getCreditScore()
.then(result => newUser.creditScore = result)
.then(() => newUser.save())
.then(() => newUser);
})
).then(data =>
console.log(data); // this will be the newUsers Array
);
}
This assumes, though, that you don't need to wait for all the $http.get before running the user.save() - in fact this may be a little (very little) more performant as the newUser.save and $http.get will run in tandem
Ok, I know your meaning, you want your every element of your array do something that is async.
So you can use map and Promise.all. Here is my code:
const asyncFunction = (item, cb) => {
setTimeout(() => {
console.log(`done with ${item}`);
cb();
}, 1000);
}
let requests = [1, 2, 3].map((item) => {
return new Promise((resolve) =>{
asyncFunction(item, resolve);
});
});
Promise.all(requests).then(() => console.log('done'));

Categories

Resources