Array pushes data fetched from an api twice - javascript

I'm making a call to an api and I'm using async/await. I'm pushing the response data to a global array, but for some reason it is pushing it twice. I'm making two fetch requests, receiving the response, pushing it to an array, and then console logging the data array. How can I prevent it from pushing the data to the array twice?
var data = [];
const getData = async () => {
const f1_data = await fetch(url + countryOne.value)
.then(response => response.json())
.then((result) => {
var index = result.length - 1;
var confirmed = result[index].Confirmed;
textOne.textContent = result[index].CountryCode + " TOTAL CASES : " + confirmed;
data.push(confirmed);
console.log(data);
})
const f2_data = await fetch(url + countryTwo.value)
.then(response => response.json())
.then((result) => {
var index = result.length - 1;
var confirmed = result[index].Confirmed;
textTwo.textContent = result[index].CountryCode + " TOTAL CASES : " + confirmed;
data.push(confirmed);
console.log(data);
})
};
getData();
Expected output
Example: 56789, 89768
Output
56789, 56789, 89768, 89768

As other suggested, since you are using aysnc and await, might as well get rid of the .then() flow.
It seems like your getData() has been called multiple times, it is unsafe to keep data array outside of your getData function block. You might want to do something like this: (takes few seconds to get the api data..)
const getData = async () => {
const url = 'https://api.covid19api.com/country/'
const query = '?from=2020-12-31T00:00:00Z&to=2021-01-01T00:00:00Z';
let data = [];
const f1_data = await fetch(`${url}singapore${query}`);
const f1_json = await f1_data.json();
data.push(f1_json[0].Confirmed);
const f2_data = await fetch(`${url}malaysia${query}`);
const f2_json = await f2_data.json();
data.push(f2_json[0].Confirmed);
return data;
};
const logData = async () => {
let data = await getData();
console.log(data);
}
logData();
return the data from your async getData function.

Related

how to return values from JS .then function [using arrow function]

Unsure how to access the values date and prices else where in the script
fetch("fileName")
.then(response => response.json())
.then(data => {
var date = data.JSONdata[data.JSONdata.length - 1].date
var prices = data.JSONdata[data.JSONdata.length - 1].prices
})
You can simply rewrite your code to use async/await:
const response = await fetch("fileName");
const data = await response.json();
const date = data.JSONdata[data.JSONdata.length - 1].date;
const prices = data.JSONdata[data.JSONdata.length - 1].prices;
EDIT: To use this asynchronous code, you have to define the function in which you use this code as async:
async function yourFunction() {
// your async code from above
}
// await async function where it's called
const functionResult = await yourFunction();
You have to make every function in your callstack async up to the level where you no longer care about the response. So the function which calls "yourFunction()" has to be async aswell. And so on.
You can use await to get the Promise result. Please refer MDN docs
In the given example date and prices are hard-coded which will be fetched via API calls in real-scenario
const date = Date.now();
const prices = [1000, 2000];
const resolvedProm = Promise.resolve({date, prices});
let thenProm = resolvedProm.then(({date, prices}) => {
return {date, prices};
});
let res = await thenProm;
console.log(res);
// {date: 1641136463085, prices: Array(2)}

loop async calls in for loop and console the data after for loop

data is coming as a blank Array [] whereas I was expecting data to be like an Array of object. If I tried setTimeout(() => {console.log(data)}, 1000) then it is console the data properly, but I don't want to use setTimeout() to console data in synchronous manner.
let data = [];
for(let i=1; i<11; i++) {
const url = `https://jsonplaceholder.typicode.com/todos/` + i;
(async () => {
let response = await fetch(url);
let resJson = await response.json();
data.push(resJson);
})();
}
console.log(data);
/*
* Working but don't want to use this
setTimeout(() => {console.log(data)}, 1000);
*/
That console.log(data) part of the code is not going to wait for the async block to be resolved and instead will be executed synchronously. Instead you could first push requests to one array and then resolve them with Promise.all and do all of that inside of IIFE.
(async() => {
let requests = [];
for (let i = 1; i < 11; i++) {
requests.push(fetch(`https://jsonplaceholder.typicode.com/todos/${i}`));
}
const responses = await Promise.all(requests)
const data = await Promise.all(responses.map(res => res.json()))
console.log(data)
})();

loadData() returning empty array

I'm trying to return an array of objects to a function, and looking at the debugger, it looks like it just returns an empty array.
Here's the function that I'm hoping to use:
// load from json file
loadData(filepath) {
let rooms = [];
fetch(filepath)
.then(
res => res.json()
)
.then(function (data) {
// console.log(data);
for (let i = 0; i < data.rooms.length; i++) {
rooms.push(data.rooms[i]);
}
});
return rooms;
}
I go into the debugger, and I see the rooms from data.rooms getting pushed to my local rooms object, but when I get back up to my init() function that's supposed to use this rooms data:
let rooms = this.loadData(this.datapath); // TODO: make games load from json data
for (let i = 0; i < rooms.length; i++) {
this.addRoom(rooms[i].name, rooms[i].getText, rooms[i].prompts, rooms[i].requirements);
}
It looks like it's passed an empty array. Is there a scoping issue or something I'm missing? I'm not very experienced with fetch/promises/JSON.
Happy to provide any further info that may be useful. Thanks!
The problem is that your local variable rooms will be returned before the async fetch finish.
You can just simple await the fetch statements and make the function async:
async loadData(filepath) {
let rooms = [];
const response = await fetch(filepath);
const data = await response.json();
console.log(data);
for (let i = 0; i < data.rooms.length; i++) {
rooms.push(data.rooms[i]);
}
return rooms;
}
let rooms = await this.loadData(this.datapath);
Try this, if it did not work let me know.
// load from json file
async function call() {
const loadData = async filepath => {
let rooms = [];
return await fetch(filepath)
.then(res => res.json())
.then(async data => {
rooms = await [...data];
return rooms;
});
};
let x = await loadData("https://jsonplaceholder.typicode.com/posts");
console.log("TEST: ", x);
}
call();

Async call with fetch not waiting to done and going right away to the then

I am trying to sequence the run of couple of functions one after the other.
The code below calculates correctly the pages but does not return anything to its caller.
const rp = require('request-promise');
const cheerio = require('cheerio');
const fetch = require('node-fetch');
async function getNumberOfPages(url) {
const pageUrl = url + "1";
rp(pageUrl)
.then(function(html) {
return fetch(pageUrl)
.then(response => response.text())
.then(html => {
const entriesPerPage = 20;
const $ = cheerio.load(html);
let totalEntries = $(".Aa").first().text()
let pagesNumber = Math.ceil(totalEntries / entriesPerPage);
return pagesNumber;
})
})
}
Actually, the caller enters right away in B) and the console log is "Number pages: undefined".
function loadAllFeedbacks(url) {
getNumberOfPages(url)
// A) Get first the pages number
.then(result => {
console.log("Number pages: " + result);
})
// B) Executes this next
.then(() => {
rp(url)
I can't figure out why. It seems that I should do something with the fetch but don't know how.
Also wondering if there is any point to use rp and fetch together, seems one only suffices.
Thank you
getNumberOfPages gets to the end without returning anything, so it returns a promise that resolves as undefined.
You need to explicitly return something.
async function getNumberOfPages(url) {
const pageUrl = url + "1";
return rp(pageUrl)
// etc
Now, however, you are returning a promise without using await at any point. So using the async keyword is pointless.
function getNumberOfPages(url) {
const pageUrl = url + "1";
return rp(pageUrl)
// etc
Or you could rewrite the function to make use of await
async function getNumberOfPages(url) {
const pageUrl = url + "1";
const value_never_used = await rp(pageUrl);
const response = await fetch(pageUrl);
const html = await response.text();
const entriesPerPage = 20;
const $ = cheerio.load(html);
let totalEntries = $(".Aa").first().text();
let pagesNumber = Math.ceil(totalEntries / entriesPerPage);
return pagesNumber;
}
There's no point in using request-promise and node-fetch, hence value_never_used

Waiting for all firebase call to finish with map function

I'm fetching my user data and the map function is called several times for each user. I want to wait until all data was pushed to the array and then manipulate the data. I tried using Promise.all() but it didn't work.
How can I wait for this map function to finish before continuing?
Needless to say that the array user_list_temp is empty when printed inside the Promise.all().
const phone_list_promise_1 = await arrWithKeys.map(async (users,i) => {
return firebase.database().ref(`/users/${users}`)
.on('value', snapshot => {
user_list_temp.push(snapshot.val());
console.log(snapshot.val());
})
}
);
Promise.all(phone_list_promise_1).then( () => console.log(user_list_temp) )
I changed the code to this but I still get a wrong output
Promise.all(arrWithKeys.map(async (users,i) => {
const eventRef = db.ref(`users/${users}`);
await eventRef.on('value', snapshot => {
const value = snapshot.val();
console.log(value);
phone_user_list[0][users].name = value.name;
phone_user_list[0][users].photo = value.photo;
})
console.log(phone_user_list[0]);
user_list_temp.push(phone_user_list[0]);
}
));
console.log(user_list_temp); //empty array
}
It is possible to use async/await with firebase
This is how I usually make a Promise.all
const db = firebase.database();
let user_list_temp = await Promise.all(arrWithKeys.map(async (users,i) => {
const eventRef = db.ref(`users/${users}`);
const snapshot = await eventref.once('value');
const value = snapshot.value();
return value;
})
);
This article gives a fairly good explanation of using Promise.all with async/await https://www.taniarascia.com/promise-all-with-async-await/
Here is how I would refactor your new code snippet so that you are not mixing promises and async/await
let user_list_temp = await Promise.all(arrWithKeys.map(async (users,i) => {
const eventRef = db.ref(`users/${users}`);
const snapshot= await eventRef.once('value');
const value = snapshot.val();
console.log(value);
phone_user_list[0][users].name = value.name; // should this be hardcoded as 0?
phone_user_list[0][users].photo = value.photo; // should this be hardcoded as 0?
console.log(phone_user_list[0]);
return phone_user_list[0]; // should this be hardcoded as 0?
})
);
console.log(user_list_temp);
Here is a simple example that uses fetch, instead of firebase:
async componentDidMount () {
let urls = [
'https://jsonplaceholder.typicode.com/todos/1',
'https://jsonplaceholder.typicode.com/todos/2',
'https://jsonplaceholder.typicode.com/todos/3',
'https://jsonplaceholder.typicode.com/todos/4'
];
let results = await Promise.all(urls.map(async url => {
const response = await fetch(url);
const json = await response.json();
return json;
}));
alert(JSON.stringify(results))
}
If I understand your question correctly, you might consider revising your code to use a regular for..of loop, with a nested promise per user that resolves when the snapshot/value for that user is available as shown:
const user_list_temp = [];
/*
Use regular for..of loop to iterate values
*/
for(const user of arrWithKeys) {
/*
Use await on a new promise object for this user that
resolves with snapshot value when value recieved for
user
*/
const user_list_item = await (new Promise((resolve) => {
firebase.database()
.ref(`/users/${users}`)
.on('value', snapshot => {
/*
When value recieved, resolve the promise for
this user with val()
*/
resolve(snapshot.val());
});
}));
/*
Add value for this user to the resulting user_list_item
*/
user_list_temp.push(user_list_item);
}
console.log(user_list_temp);
This code assumes that the enclosing function is defined as an asynchronous method with the async keyword, seeing that the await keyword is used in the for..of loop. Hope that helps!

Categories