firebase storage always return Promise - javascript

I have an application on vue 3. I need to get a link to a document from the repository. At the moment, I always get a promise, which is how it should be. But I should get a link to the document, but it doesn't. Why is this happening?
async FetchData({ state, commit }, to) {
try {
commit("setLoading", true);
const q = query(collection(db, to));
await onSnapshot(q, (querySnapshot) => {
const data = [];
querySnapshot.forEach((doc) => {
let films = async (to) => {
const starsRef = ref(storage, `images/${doc.id}/poster.png`);
return await getDownloadURL(starsRef);
};
// const poster=`gs://cell-11ef4.appspot.com/images/${doc.id}/poster.png`
let item = {
id: doc.id,
name: doc.data().name,
slug: doc.data().slug,
country: doc.data().country,
duration: doc.data().duration,
year: doc.data().year,
video: doc.data().video,
genres: doc.data().genres,
actors: doc.data().actors,
poster: to === "films" ? films() : null,
// BipPoster: url,
};
data.push(item);
// Get the download URL
});
commit("setData", { data, to });
});
} catch (err) {
console.log(err);
} finally {
commit("setLoading", false);
}
}
let url = async () => {
let url;
const starsRef = ref(storage, `images/${doc.id}/poster.png`);
await getDownloadURL(starsRef).then((p) => {
url = p;
});
return url;
};
What i get

You should use Promise.all() as follows:
const promises = [];
querySnapshot.forEach((doc) => {
const starsRef = ref(storage, `images/${doc.id}/poster.png`);
promises.push(getDownloadURL(starsRef));
});
const urlsArray = await Promise.all(promises);

Map the documents collection to an array of promises that resolve with the full item data, including download URL and await them all with Promise.all().
You're also registering a real-time updates listener which seems counter to what you seem to want FetchData to do. I would suggest you want to use getDocs() instead of onSnapshot()
async FetchData({ state, commit }, to) {
try {
commit("setLoading", true);
// `onSnapshot` registers a real-time updates listener,
// use `getDocs` to retrieve documents
const { docs } = await getDocs(query(collection(db, to)));
// Map over the `docs` array and return fully hydrated objects
const data = await Promise.all(
docs.map(async (doc) => ({
...doc.data(),
id: doc.id,
poster:
to === "films"
? await getDownloadURL(ref(storage, `images/${doc.id}/poster.png`))
: null,
}))
);
commit("setData", { data, to });
} catch (err) {
console.error(err);
} finally {
commit("setLoading", false);
}
},
If you did want to register a real-time updates listener, do so in an effect hook where you can also remove the listener in a cleanup
useEffect(() => {
// Return the unsub function as cleanup
return onSnapshot(query(collection(db, to)), async ({ docs }) => {
try {
const data = await Promise.all(
docs.map(async (doc) => ({
...doc.data(),
id: doc.id,
poster:
to === "films"
? await getDownloadURL(
ref(storage, `images/${doc.id}/poster.png`)
)
: null,
}))
);
commit("setData", { data, to });
} catch (err) {
console.error(err);
}
});
}, [to]);

async FetchData({ state, commit }, to) {
try {
const q = query(collection(db, to));
await onSnapshot(q, (querySnapshot) => {
const allPromises = querySnapshot.docs.map(async (doc) => {
let item = {
id: doc.id,
name: doc.data().name,
slug: doc.data().slug,
country: doc.data().country,
duration: doc.data().duration,
year: doc.data().year,
video: doc.data().video,
genres: doc.data().genres,
actors: doc.data().actors
};
if (to === "films") {
const starsRef = ref(storage, `images/${doc.id}/poster.png`);
item.poster = await getDownloadURL(starsRef);
}
return item;
});
Promise.all(allPromises)
.then((data) => commit("setData", { data, to }))
.catch(console.error);
});
} catch (err) {
console.error(err);
}
}

Related

Async await in for loop

I have this function and I'm trying to push objects into the "groupData" array and then return the response object but when the function successfully runs, I get "response" as a null object. What is wrong with my code can anyone help? How can I make the function to wait for the map function to finish and then return.
const groupList = async (io, socket, userid) => {
var response = {};
try {
var groupData = [];
ddb.get({
TableName: "Tablename",
Key: { Username: userid },
})
.promise()
.then(async (user) => {
if (Object.keys(user).length === 0) {
} else {
const groups = user.Item.Chatgroups;
groups.map((g) => {
ddb.get({
TableName: "Tablename",
Key: { ChatID: g },
})
.promise()
.then(async (data) => {
groupData.push({
ChatID: g,
Chatname: data.Item.Chatname,
Group: data.Item.Group
});
})
.catch((err) => {
console.log("Chat group not found");
});
})
response["groups"] = groupData;
}
})
.catch((err) => {
response["code"] = 400;
response["message"] = "Something Went Wrong";
});
} catch (error) {
console.log(error);
} finally {
return response;
}
};
Use Promise.all and if you use async then make use of await.
Here is how your code could look. I removed the error handling -- first test this and when it works, start adding back some error handling (with try/catch):
const groupList = async (io, socket, Username) => {
const user = await ddb.get({
TableName: "Tablename",
Key: { Username },
}).promise();
if (!Object.keys(user).length) return {};
return {
groups: await Promise.all(user.Item.Chatgroups.map(async ChatID => {
const { Item: { Chatname, Group } } = await ddb.get({
TableName: "Tablename",
Key: { ChatID },
}).promise();
return { ChatID, Chatname, Group };
}))
};
};
I searched too long for this 😭
for await (item of items) {}

Is there a way to make an api call within a map of another api call?

I know the title is quite confusing, I wasn't sure how to word it better. What I am trying to do is to fetch some items, map through those items to display them, but the problem is that one of those items has a value of what needs to be another api call to access it.
This is what I'm trying to do:
First of all I am storing an empty state, which later on becomes the data of the fetched items:
const [data, setData] = useState([]);
I'm using axios to fetch and store the data:
const fetchItems = () => {
axios("https://swapi.dev/api/people")
.then((response) => {
console.log(response.data.results);
const newData = response.data.results.map((item) => ({
name: item.name,
homeworld: () => {
axios.get(item.homeworld).then((response) => {
response.data.results;
});
},
}));
setData(newData);
})
.catch((error) => {
console.log("error", error);
});
};
It works with the name because it's a simple value. However, the homeworld includes a link that needs to be called once again in order to access it, instead of being a simple value like the name in this case. How can I call it and access what values are held within that link, and display them instead of just displaying the url?
I hope this can help you:
const [data,setData] = useState([])
const fetchItems = () => {
axios("https://swapi.dev/api/people")
.then(response => {
console.log(response.data.results);
const { results } = response.data;
for (const item of results) {
axios.get(item.homeworld).then(({data}) => {
setData([...data,{ name: item.name, homeworld: data.results }]);
});
}
})
.catch(error => {
console.log("error", error);
});
};
or with fetch:
const [data,setData] = useState([])
fetch("https://swapi.dev/api/people").then(re=>re.json())
.then(response => {
const newData = []
const { results } = response;
const newData = [];
for (const item of results) {
fetch(item.homeworld).then(re => re.json()).then((data) => {
newData.push({ name: item.name, homeworld: data });
});
}
console.log(newData)
setData(newData)
})
.catch(error => {
console.log("error", error);
});
Use Promise.all()
You can use Promise.all() method to get all the information you need by creating an array of promises by mapping the response.results array with an async function.
This is the code example
const fetchItems = async () => {
const req = await axios.get("https://swapi.dev/api/people");
const response = await req.data;
const allDataPromises = response.results.map(async (item) => {
const itemReq = await axios.get(item.homeworld);
const itemResponse = await itemReq.data;
return {
name: item.name,
homeworld: itemResponse,
};
});
const allData = await Promise.all(allDataPromises);
};
For further information about Promise.all()

Not receiving the correct data from Promises?

I have a function that fetches data from an API, and the function works correctly as intended:
const getStockData = async (stock) => {
try {
const response = await axios.get(`${BASE_URL}${stock}${KEY_URL}`);
console.log(response);
return response;
} catch (error) {
console.error('Error', error.message);
}
};
And I have another function that gets data from my firebase which then passes in the .ticker into the function above however when I log the response from the promise the data is returned null
Is there a reason why its not working as intended?
const getMyStocks = async () => {
let promises = [];
let tempData = [];
const querySnapshot = await getDocs(collection(db, 'myStocks'));
querySnapshot.forEach((doc) => {
console.log(doc.data().ticker);
promises.push(
getStockData(doc.data().ticker).then((res) => {
console.log(res);
tempData = {
id: doc.id,
data: doc.data(),
info: res.data,
};
})
);
getMyStocks must return the resolution of the promises it creates...
// to reduce nested promises, this takes an FB doc and adds the getStockData query to it
const getFBAndTickerData = async doc => {
return getStockData(doc.data().ticker).then(res => {
console.log(res);
return {
id: doc.id,
data: doc.data(),
info: res.data,
};
});
}
const getMyStocks = async () => {
const querySnapshot = await getDocs(collection(db, 'myStocks'));
let promises = querySnapshot.docs.map(doc => {
console.log(doc.data().ticker);
return getFBAndTickerData(doc);
});
return Promise.all(promises);
}

Multiple API calls with Promise.all and dispatch an action

I want to call multiple API's and store each response data in an object then I want to dispatch this response object but I'm getting undefined.
Below is the code I tried. May I know where I'm doing wrong?
/* COMPONENT.JSX */
componentDidMount() {
callApis(this.props.products, this.props.profileId);
}
/* API.JS */
const getContactDetails = (http, profileId) =>
(http.get(`https://www.fakeurl.com/${profileId}/contact`));
const getProductDetails = (http, profileId) =>
(http.get(`https://www.fakeurl.com/${profileId}/product`));
const callApis = (products, profileId) => (dispatch) => {
const payload = new Map();
products.forEach((product) => {
const apis = [getContactDetails, getProductDetails];
apis.map(api => api(http, profileId));
Promise.all(apis)
.then((response) => {
const apiData = {
contactData: getParsedContactData(response[0]),
productData: getParsedProductData(response[1])
};
if (payload.get(product.token)) {
payload.get(companion.token).push(apiData);
} else {
payload.set(product.token, [apiData]);
}
})
.catch(err => {
throw ('An error occurred ', err);
});
});
dispatch({ type: FETCH_API_DATA, payload: payload });
}
I expect the dispatch will be called after all API's were resolved, get parsed, and map into the payload object then it should dispatch.
Array.map returns a new Array, which you are discarding
you're calling dispatch before any of the asynchronous code has run
A few minor changes are required
/* API.JS */
const getContactDetails = (http, profileId) => http.get(`https://www.fakeurl.com/${profileId}/contact`);
const getProductDetails = (http, profileId) => http.get(`https://www.fakeurl.com/${profileId}/product`);
const callApis = (products, profileId) => (dispatch) => {
const payload = new Map();
// *** 1
const outerPromises = products.map((product) => {
const apis = [getContactDetails, getProductDetails];
// *** 2
const promises = apis.map(api => api(http, profileId));
// *** 3
return Promise.all(promises)
.then((response) => {
const apiData = {
contactData: getParsedContactData(response[0]),
productData: getParsedProductData(response[1])
};
if (payload.get(product.token)) {
payload.get(companion.token).push(apiData);
} else {
payload.set(product.token, [apiData]);
}
})
.catch(err => {
throw ('An error occurred ', err);
});
}));
// *** 4
Promise.all(outerPromises)
.then(() => dispatch({
type: FETCH_API_DATA,
payload: payload
})
)
.catch(err => console.log(err));
}
rather than procucts.forEach, use products.map
capture the promises in apis.map to use in Promise.all
return Promise.all so the outer Promises can be waited for
Promise.all on the outer promises, to wait for everything to complete.
const callApis = (products, profileId) => async (dispatch) => { // use async function
const payload = new Map();
for (const product of products) {
const apis = [getContactDetails, getProductDetails];
apis.map(api => api(http, profileId));
await Promise.all(apis) // await all promise done
.then((response) => {
const apiData = {
contactData: getParsedContactData(response[0]),
productData: getParsedProductData(response[1])
};
if (payload.get(product.token)) {
payload.get(companion.token).push(apiData);
} else {
payload.set(product.token, [apiData]);
}
})
.catch(err => {
throw ('An error occurred ', err);
});
}
dispatch({ type: FETCH_API_DATA, payload: payload }); // dispatch will be executed when all promise done
}

Correct way to cascate promises for arrays (mongoose example)

I need to understand the correct way to cascade promises using mongoose.
My function createCustomerBills with receive a list of customer id's that, for each one, I need to create a simple bill.
Here is the code:
const createCustomerBill = (customer) => {
return BillModel.create({
customer_id: customer.id,
value: 100
});
}
const createCustomerBills => (customerIds) => {
let criteria = {
_id: { $in: customerIds }
};
return CustomerModel.find(criteria)
.then(result => {
return result.map(customer => {
return createCustomerBill(customer);
}
})
.then(result => {
return CustomerModel.update(
{ _id: customer.id },
{ status: "BillCreated" }
);
});
}
Here is the steps:
1. Get list of all customers
2. For each customer, create the bill
3. For each bill created, update the customer status
I need an advice if this is the correct method of doing it and possible drawbacks.
Because you are mapping the result of CustomerModel.find, returning that array wont wait for the promises to complete to run the next .then, as an array is not a Promise
That's where Promise.all comes in
Also, as you need to update each bill individually, that part of the promise chain needs to be inside the .map iteration
const createCustomerBill = customer =>
BillModel.create({
customer_id: customer.id,
value: 100
});
const createCustomerBills => customerIds => {
let criteria = {
_id: { $in: customerIds }
};
return CustomerModel.find(criteria)
.then(result =>
Promise.all(
result.map(customer => createCustomerBill(customer)
.then(result => CustomerModel.update({ _id: customer.id }, { status: "BillCreated" }))
)
)
)
.then(result => {
// all bills are now processed
});
}
Here is a possible working way, IMO
const createCustomerBill = (customer) => {
return BillModel.create({
customer_id: customer.id,
value: 100,
});
}
const createCustomerBills => (customerIds) => {
const criteria = {
_id: {
$in: customerIds,
},
};
return CustomerModel.find(criteria)
.then((customers) => Promise.all(customers.map(x => createCustomerBill(x)
.then(() => CustomerModel.update({
_id: x.id,
}, {
status: "BillCreated"
})))))
.then(() => console.log('over'))
.catch(e => console.log(e));
}
And here using the awesome async/await
const createCustomerBill = customer => BillModel.create({
customer_id: customer.id,
value: 100,
});
const updateCustomer = customer => CustomerModel.update({
_id: x.id,
}, {
status: 'BillCreated',
});
const createBillAndUpdateCustomer = async(customer) => {
await createCustomerBill(customer);
await updateCustomer(customer);
};
const createCustomerBills => async(customerIds) => {
const customers = await CustomerModel.find({
_id: {
$in: customerIds,
},
});
await Promise.all(customers.map(x => createBillAndUpdateCustomer(x)));
console.log('over');
}

Categories

Resources