Making parallel async await calls in ReactJS - javascript

Currently each of the value is set after setting the previous value, the async calls are not executed in parrallel. How do I make these calls execute in parallel?
const [index, setIndex] = useState(0);
const [roll, setRollNo] = useState(1);
const [sem, setSemester] = useState(1);
useEffect(() => {
getMyValue();
}, []);
const getMyValue = async () => {
try {
setIndex(JSON.parse(await AsyncStorage.getItem('#branch')) || 0);
setSemester(JSON.parse(await AsyncStorage.getItem('#sem')) || 1);
setRollNo(JSON.parse(await AsyncStorage.getItem('#roll')) || 1);
} catch (e) {
// console.log(e);
}
};

You can use Promise.all
const [index, semester, roll] = await Promise.all([
AsyncStorage.getItem('#branch'),
AsyncStorage.getItem('#sem'),
AsyncStorage.getItem('#roll')]);
setIndex(JSON.parse(index) || 0);
setSemester(JSON.parse(semester) || 1);
setRollNo(JSON.parse(roll) || 1);
Or if you like to turn such thing into mapping monstrosity as recommended in the answers there you go...
const params = ['#branch', '#sem', '#roll'];
const defaultValues = [0, 1, 1];
const [index, semester, roll] = await Promise.all(
params.map(AsyncStorage.getItem))
.then((values) => values.map((pr, index) => JSON.parse(pr) || defaultValues[index]));
setIndex(index);
setSemester(semester);
setRollNo(roll);

To execute several promises in parallel you need organize them as array and execute unsing Promise.all:
const [index, setIndex] = useState(0);
const [roll, setRollNo] = useState(1);
const [sem, setSemester] = useState(1);
useEffect(() => {
getMyValue();
}, []);
const getMyValue = async () => {
try {
const itemsArr = ['#branch', '#sem', '#roll']
const result = await Promise.all(promisesArr.map(item => AsyncStorage.getItem(item)))
setIndex(JSON.parse(result[0]) || 0);
setSemester(JSON.parse(result[1]) || 1);
setRollNo(JSON.parse(result[2]) || 1);
} catch (e) {
// console.log(e);
}
};

React JS does not batch the state updates if the event handler is async.
In your example, as you are await-ing on AsyncStorage.getItem, they are not batched.
You can #Józef 's solution to batch them up
Reference: https://github.com/facebook/react/issues/14259#issuecomment-450118131

Using for await of ...
We use an array of jobs to getItem, then after await, we can set the state of each item based on its type.
#Jozef's answer is also a great option as well, very good use of array deconstruction there
const response = []
const jobs = ["#branch", "#sem", "#roll"]
for await (item of jobs) {
const res = await AsyncStorage.getItem(item);
response.push({
type: item,
res
})
}
/**
response
[
{ type: "#branch", res: <res> },
{ type: "#sem", res: <res> },
{ type: "#roll", res: <res> }]
*/
response.forEach(item => {
const { type, res } = item;
if (type === "#branch") {
setIndex(JSON.parse(res))
} else if (type === "#semester") {
setSemester(JSON.parse(res))
} else if (type === "#roll") {
setRollNo(JSON.parse(res))
}
})

you don't need a async await here as you don't want to wait just use a promise instead
const getMyValue2 = () => {
try {
Promise.all([
Promise.resolve( AsyncStorage.getItem('#branch')),
Promise.resolve( AsyncStorage.getItem('#sem')),
Promise.resolve( AsyncStorage.getItem('#roll'))
]).then(data => {
setIndex(JSON.parse(data[0] || 0));
setRollNo(JSON.parse(data[1] || 1));
setSemester(JSON.parse(data[2] || 1));
});
} catch (e) {
// console.log(e);
}
};

Related

How to wait for DOM commit in react

I am working on a ReactJS app that uses Webassembly with Emscripten. I need to run a series of algorithms from Emscripten in my Js and I need to show the results as they come, not altogether at the end. Something like this:
Run the first algo
Show results on-screen form first algo
Run the second algo AFTER the results from the first algo are rendedred on the screen (so that the user can keep checking them)
Show results on screen from the second algo, and so on.
To achieve this I'm using "UseEffect" like this:
useEffect(() => {
(
async function AlgoHandler(){
if (methodsToRun.length <= 0) {
setGlobalState('loadingResults', false);
return;
}
const method = methodsToRun[0];
let paramsTypes = method[1].map((param) => param[0][2]);
let runAlgo = window.wasm.cwrap(method[0], 'string', paramsTypes);
let params = method[1].map(
(param) => document.getElementById(param[0][0]).value
);
let result = await runAlgo(...params);
setGlobalState('dataOutput', (prev) => [...prev, ...JSON.parse(result)]);
await sleep(100);
setGlobalState('methodsToRun', (prev) => prev.filter(m => m != method));
})();
}, [methodsToRun]);
The problem is that this code "kind of" works, this is because useEffect ensures rendering before my callback, but DOM commits don't always happen, and the user doesn't see any updates on the UI. I would like to make it wait for the DOM commit of the component, not just the rerender, this is a component life-cycle in react:
So, any ideas on how I can achieve this?
The DOM commits always happen (React guarantees that), but the browser may not have had a chance to paint those commits.
You could use the usual setTimeout(/*...*/, 0) trick:
useEffect(() => {
setTimeout(() => {
async function AlgoHandler(){
if (methodsToRun.length <= 0) {
setGlobalState('loadingResults', false);
return;
}
const method = methodsToRun[0];
let paramsTypes = method[1].map((param) => param[0][2]);
let runAlgo = window.wasm.cwrap(method[0], 'string', paramsTypes);
let params = method[1].map(
(param) => document.getElementById(param[0][0]).value
);
let result = await runAlgo(...params);
setGlobalState('dataOutput', (prev) => [...prev, ...JSON.parse(result)]);
await sleep(100);
setGlobalState('methodsToRun', (prev) => prev.filter(m => m != method));
})();
}, 0);
}, [methodsToRun]);
That's not a guarantee, though. If you want to be really, really sure, wait for a requestAnimationFrame callback before doing the setTimeout(/*...*/, 0) call:
useEffect(() => {
requestAnimationFrame(() => {
setTimeout(() => {
async function AlgoHandler(){
if (methodsToRun.length <= 0) {
setGlobalState('loadingResults', false);
return;
}
const method = methodsToRun[0];
let paramsTypes = method[1].map((param) => param[0][2]);
let runAlgo = window.wasm.cwrap(method[0], 'string', paramsTypes);
let params = method[1].map(
(param) => document.getElementById(param[0][0]).value
);
let result = await runAlgo(...params);
setGlobalState('dataOutput', (prev) => [...prev, ...JSON.parse(result)]);
await sleep(100);
setGlobalState('methodsToRun', (prev) => prev.filter(m => m != method));
})();
}, 0);
});
}, [methodsToRun]);
You know from the fact rAF called your callback that the browser is just about to paint, so the setTimeout should be sufficient to wait until after the painting is complete.
This is sufficiently gnarly and magic-esque that it's likely best to wrap it in a hook:
const useAfterPaintEffect = (callback, deps) => {
// (Explanation of how this works goes here)
useEffect(() => {
requestAnimationFrame(() => {
setTimeout(() => {
callback();
}, 0);
});
}, deps);
};
then
useAfterPaintEffect(() => {
async function AlgoHandler(){
if (methodsToRun.length <= 0) {
setGlobalState('loadingResults', false);
return;
}
const method = methodsToRun[0];
let paramsTypes = method[1].map((param) => param[0][2]);
let runAlgo = window.wasm.cwrap(method[0], 'string', paramsTypes);
let params = method[1].map(
(param) => document.getElementById(param[0][0]).value
);
let result = await runAlgo(...params);
setGlobalState('dataOutput', (prev) => [...prev, ...JSON.parse(result)]);
await sleep(100);
setGlobalState('methodsToRun', (prev) => prev.filter(m => m != method));
})();
}, [methodsToRun]);
Note that that hook doesn't allow for cleanup. To do that, you'd have to make your callback return another callback (to do the actual work) and an optional cleanup callback.
const useAfterPaintEffect = (callback, deps) => {
// (Explanation of how this works goes here)
const [run, cleanup] = callback();
useEffect(() => {
requestAnimationFrame(() => {
setTimeout(() => {
run();
}, 0);
});
return cleanup;
}, deps);
};
then
useAfterPaintEffect(() => {
return [
() => {
async function AlgoHandler(){
if (methodsToRun.length <= 0) {
setGlobalState('loadingResults', false);
return;
}
const method = methodsToRun[0];
let paramsTypes = method[1].map((param) => param[0][2]);
let runAlgo = window.wasm.cwrap(method[0], 'string', paramsTypes);
let params = method[1].map(
(param) => document.getElementById(param[0][0]).value
);
let result = await runAlgo(...params);
setGlobalState('dataOutput', (prev) => [...prev, ...JSON.parse(result)]);
await sleep(100);
setGlobalState('methodsToRun', (prev) => prev.filter(m => m != method));
})();
},
// This specific one doesn't need a cleanup callback
];
}, [methodsToRun]);

I am facing problem chaining the async code in Javascript

I am trying to execute allCountryData and return a promise its working fine but after allCountryData is done executing I want to perform a operation on that returned data / or allCountryDataArray and store the highest values in arrayOfHighestCases
Note I can't chain the other login in allCountryData.
Please help let me know if you need any more details
export const allCountryDataArray = [];
export const arrayOfHighestCases = [];
const allCountryData = async () => {
sendHTTP()
.then((res) => {
return res.response;
})
.then((res) => {
allCountryDataArray.push(...res);
return allCountryDataArray;
});
return await allCountryDataArray;
// Highest Cases
};
The code is below is not working
const highestCasesData = async () => {
// const allCountryDataArrayy = await allCountryData();
// allCountryData()
// .then((data) => {
// console.log(arrayOfHighestCases[0]);
// })
// .then((res) => {
const np = new Promise((res, rej) => {
res(allCountryData());
});
return np.then((res) => {
console.log(res);
const arrayofHigh = allCountryDataArray.sort((a, b) => {
if (a.cases.total < b.cases.total) {
return 1;
} else if (a.cases.total > b.cases.total) {
return -1;
} else {
return 0;
}
});
console.log(arrayofHigh);
const slicedArray = arrayofHigh.slice(0, 6);
for (const eachHighCase of slicedArray) {
arrayOfHighestCases.push(eachHighCase);
}
console.log(arrayOfHighestCases);
return arrayOfHighestCases;
});
// });
};
highestCasesData();
Filling global arrays with async data is a way into timing conflicts. Bugs where the data ain't there, except when you look it is there and yet another question here on my SO about "Why can't my code access data? When I check in the console everything looks fine, but my code ain't working."
If you want to store something, store Promises of these arrays or memoize the functions.
const allCountryData = async () => {
const res = await sendHTTP();
return res.response;
};
const highestCasesData = async () => {
const allCountryDataArray = await allCountryData();
return allCountryDataArray
.slice() // make a copy, don't mutate the original array
.sort((a, b) => b.cases.total - a.cases.total) // sort it by total cases DESC
.slice(0, 6); // take the first 6 items with the highest total cases
}
This is working please let me know if I can make some more improvements
const allCountryData = async () => {
return sendHTTP()
.then((res) => {
return res.response;
})
.then((res) => {
allCountryDataArray.push(...res);
return allCountryDataArray;
});
// Highest Cases
};
const highestCasesData = async () => {
return allCountryData().then((res) => {
console.log(res);
const arrayofHigh = allCountryDataArray.sort((a, b) => {
if (a.cases.total < b.cases.total) {
return 1;
} else if (a.cases.total > b.cases.total) {
return -1;
} else {
return 0;
}
});
console.log(arrayofHigh);
const slicedArray = arrayofHigh.slice(0, 6);
for (const eachHighCase of slicedArray) {
arrayOfHighestCases.push(eachHighCase);
}
console.log(arrayOfHighestCases);
return arrayOfHighestCases;
});
};
highestCasesData();

React script stop working after changing API call

I have a script which calls API from React and then triggers email notification function.
I was changing one part of it to call whole array of parameters instead of calling one parameter after another.
Here is part before change(working one). Console log shows correct response and I receive email notification as well.
const getApiData = () => {
const apiCall = (symbol) => {
return `https://min-api.cryptocompare.com/data/pricemulti?fsyms=${symbol}&tsyms=USD&api_key=API-KEY-HERE`
}
const MAX_CHARACKTERS = 300
let bucketArray = ['']
for (let i=0; i < assets.length - 1; i += 1) {
const symbol = `${bucketArray[bucketArray.length - 1]},${assets[i]}`
if (i === 0) {
bucketArray[0] = assets[i]
continue
}
if (symbol.length < MAX_CHARACKTERS) {
bucketArray[bucketArray.length - 1] = symbol
} else {
bucketArray[bucketArray.length] = assets[i]
}
}
const getData = () => {
Promise.all(
bucketArray.map(req => {
return axios(apiCall(req))
.then(({ data }) => data)
})
).then((data) => setDataApi(data))
}
getData()
};
Here is problematic one.
const getApiData = () => {
const getString = symbol =>
`https://min-api.cryptocompare.com/data/pricemulti?fsyms=${symbol}&tsyms=USD&api_key=API-KEY-HERE`;
function getAxious(id) {
const url = getString(id);
return axios.get(url);
}
const BUCKET_SIZE = 150;
const bucketArray = assets.reduce(
(arr, rec) => {
if (arr[arr.length - 1].length < BUCKET_SIZE) {
arr[arr.length - 1] = [...arr[arr.length - 1], rec];
return arr;
}
return [...arr, [rec]];
},
[[]]
);
bucketArray
.reduce((acc, rec) => {
return acc.then(results => {
return Promise.all(
rec.map(item =>
getAxious(item).then(({ data }) => {
return {
Symbol: item,
Open: data
};
})
)
).then(x => {
return [...x, ...results];
});
});
},
Promise.resolve([]))
.then(res => {
setDataApi(res);
});
};
Here in console I receive empty array - [] no errors showed, but email notification also stops from working.
I'm changing the code since I need to call whole array from API in one call. Before I was calling one symbol after another.
What I did wrong that console doesn't show the correct response?
EDIT1
Here is bucketArray value
const assets = ['ADA','KAVA','DOGE'];
I was not able to understand completely, but I think you want to collect all the results together and set it to the data using setDataApi.
Check the below code and let me know if it helps:
async function getApiData() {
const getString = (arr) =>
`https://min-api.cryptocompare.com/data/pricemulti?fsyms=${arr.join(
","
)}&tsyms=USD&api_key=API_KEY`;
function getAxious(arr) {
const url = getString(arr);
return axios.get(url);
}
const BUCKET_SIZE = 150;
const bucketArray = assets.reduce(
(arr, rec) => {
if (arr[arr.length - 1].length < BUCKET_SIZE) {
arr[arr.length - 1] = [...arr[arr.length - 1], rec];
return arr;
}
return [...arr, [rec]];
},
[[]]
);
const res = await getAxious(bucketArray);
console.log("res", res);
return res;
// after this you can set setDataApi(res);
}
// keep this useEffect sepearate
const [timer, setTimer] = useState(null);
useEffect(() => {
async function getApiDatahandler() {
const res = await getApiData();
console.log(res);
const timerId = setTimeout(() => {
getApiDatahandler();
}, 1000 * 60);
setTimer(timerId);
setDataApi(res)
// set the data setDataApi(res);
}
getApiDatahandler();
return () => {
window.clearTimeout(timer);
};
}, []);
// useEffect(() => {
// const timerId = setTimeout(() => {
// getApiData();
// }, 1000 * 60);
// }, [])
Checkout this codepen for a possible solution.
https://codepen.io/bcaure/pen/RwapqZW?editors=1011
In short, I don't know how to fix your code because it's quite a callback hell.
// Mock API and data
const bucketArray = [[{item: 'item1'}], [{item: 'item2'}], [{item: 'item3'}]];
const getAxious = item => {
return new Promise((resolve, reject) => resolve({data: 'API data'}));
}
// Return promise that combines API data + input item
const recToPromise = rec => rec.map(item => {
return new Promise((resolve, reject) => getAxious(item)
.then(data => resolve({item, data})));
});
// Flatten array
const recPromisesFlatten = bucketArray.flatMap(recToPromise);
Promise.all(recPromisesFlatten)
.then(res => {
const flattenRes = res.flatMap(({item, data}) => ({ Symbol: item, Open: data }));
console.log(JSON.Stringify(flattenRes))
});
What I'm suggesting to debug errors:
build your promise array first
then run Promise.all
then combine your data
Bonus: you can see flatMap instead of reduce for better readability.

Async/await in componentDidMount to load in correct order

I am having some troubles getting several functions loading in the correct order. From my code below, the first and second functions are to get the companyID companyReference and are not reliant on one and another.
The third function requires the state set by the first and second functions in order to perform the objective of getting the companyName.
async componentDidMount() {
const a = await this.companyIdParams();
const b = await this.getCompanyReference();
const c = await this.getCompanyName();
a;
b;
c;
}
componentWillUnmount() {
this.isCancelled = true;
}
companyIdParams = () => {
const urlString = location.href;
const company = urlString
.split('/')
.filter(Boolean)
.pop();
!this.isCancelled &&
this.setState({
companyID: company
});
};
getCompanyReference = () => {
const { firebase, authUser } = this.props;
const uid = authUser.uid;
const getUser = firebase.user(uid);
getUser.onSnapshot(doc => {
!this.isCancelled &&
this.setState({
companyReference: doc.data().companyReference
});
});
};
getCompanyName = () => {
const { firebase } = this.props;
const { companyID, companyReference } = this.state;
const cid = companyID;
if (companyReference.includes(cid)) {
const getCompany = firebase.company(cid);
getCompany.onSnapshot(doc => {
!this.isCancelled &&
this.setState({
companyName: doc.data().companyName,
loading: false
});
});
} else if (cid !== null && !companyReference.includes(cid)) {
navigate(ROUTES.INDEX);
}
};
How can I achieve this inside componentDidMount?
setState is asynchronous, so you can't determinate when the state is updated in a sync way.
1)
I recommend you don't use componentDidMount with async, because this method belongs to react lifecycle.
Instead you could do:
componentDidMount() {
this.fetchData();
}
fetchData = async () => {
const a = await this.companyIdParams();
const b = await this.getCompanyReference();
const c = await this.getCompanyName();
}
2)
The companyIdParams method doesn't have a return, so you are waiting for nothing.
If you need to wait I would return a promise when setState is finished;
companyIdParams = () => {
return new Promise(resolve => {
const urlString = location.href;
const company = urlString
.split('/')
.filter(Boolean)
.pop();
!this.isCancelled &&
this.setState({
companyID: company
}, () => { resolve() });
});
};
The same for getCompanyReference:
getCompanyReference = () => {
return new Promise(resolve => {
const { firebase, authUser } = this.props;
const uid = authUser.uid;
const getUser = firebase.user(uid);
getUser.onSnapshot(doc => {
!this.isCancelled &&
this.setState({
companyReference: doc.data().companyReference
}, () => { resolve() });
});
});
};
3)
If you want to parallelize the promises, you could change the previous code to this:
const [a, b] = await Promise.all([
await this.companyIdParams(),
await this.getCompanyReference()
]);
4)
According to your code, the third promise is not a promise, so you could update (again ;) the above code:
const [a, b] = .....
const c = this.getCompanyName()
EDIT: the bullet points aren't steps to follow
As the last api call is dependent on the response from the first 2 api calls, use a combination of Promise.all which when resolved will have the data to make the last dependent call
async componentDidMount() {
let [a, c] = await Promise.all([
this.companyIdParams(),
this.getCompanyReference()
]);
const c = await this.getCompanyName();
}

javascript with firebase timing issue

As I run this code, I wished to add the array final to the firestore -> sendGrid collection but it's always empty, although when I print it, it actually has the values.
I believe this is because of the timing issue, I always get [] -> value is just evaluated now (warning) and when I expand it it has the value.
function test() {
let today = new Date();
let addedDate = new Date(today.addDays(7));
let final = [];
let counter = 0;
let adder = new Promise(function (resolve, reject) {
db.collection("email").get()
.then((querySnapshot) => {
console.log(querySnapshot);
if (querySnapshot.empty !== true) {
querySnapshot.forEach((data) => {
console.log(data.data());
console.log(data.id);
let db2 = db.collection("email").doc(data.id);
let foodArr = [];
if (data.data() !== null) {
console.log(addedDate);
if (addedDate >= userList[0].exxpiaryDate) {
console.log("True");
}
db2.collection("list").where("expiaryDate", "<", addedDate.getTime()).get()
.then((list) => {
if (list.empty !== true) {
list.forEach((food) => {
if (food !== null) {
let temp = {
name: food.data().name,
time: food.data().expiaryDate,
};
foodArr.push(temp);
console.log(foodArr);
}
})
}
if (foodArr.length !== 0) {
let emailArr = {
email: data.data().email,
food: foodArr
};
console.log(emailArr);
final[counter] = (emailArr);
counter++;
console.log(final[0]);
}
}).catch((err) => {
console.log(err);
});
}
});
}
console.log(final);
resolve(final);
}).catch((err) => {
console.log(err);
});
});
return adder;
}
async function add() {
let add = await test();
console.log(add);
db.collection("sendGrid").add({
response: add
}).then((item) => {
console.log(item);
}).catch((err) => {
console.log(err);
});
}
Some points: (google if unsure why)
- prefer const
- return early
- clean code (eg. from console.log:s)
- cache fn calls
- functional programming is neat, look up Array.(map, filter, reduce, ...)
- destructuring is neat
- use arr[arr.length] = x; or arr.push(x), no need to manage your own counter
- short-circuit is sometimes neat (condition && expression; instead of if (condition) expression;)
- is queryResult.empty a thing? If it's a normal array, use !arr.length
- define variables in the inner most possible scope it's used in
- if having a promise in an async, make sure to return it
- prefer arrow functions
I changed the code to follow those points:
const test = ()=> {
const today = new Date();
const addedDate = new Date(today.addDays(7));
return new Promise((resolve)=> {
const final = [];
const emailsQuery = db.collection("email")
// an async/promise/then that's inside another promise, but not returned/awaited
emailsQuery.get().then((querySnapshot) => {
querySnapshot
.map(data=> ({id: data.id, data: data.data()}))
.filter(o=> o.data)
.forEach(({id, data: {email}}) => {
const db2 = db.collection("email").doc(id);
const itemsQuery = db2.collection("list").where("expiaryDate", "<", addedDate.getTime())
// another one!
itemsQuery.get().then((items) => {
const food = items.filter(o=> o).map(o=> o.data()).filter(o=> o)
.forEach(({name, expiaryDate: time})=> ({name, time}))
food.length && final.push({email, food})
}).catch(console.error);
});
// resolving before the two async ones have finished!!
resolve(final);
}).catch(console.error);
});
}
const add = async ()=> {
let response = await test();
return db.collection("sendGrid").add({response})
.then((item) => console.log('item:', item))
.catch(console.error)
}
Now, we can see that there is an issue with the async flow ("timing issue" in your words). I'll add one more best practice:
- use async/await when possible
Changing using that one makes it more clear, and solves the issue:
const test = async ()=> {
const today = new Date();
const addedDate = new Date(today.addDays(7));
const emailsQuery = db.collection("email")
const querySnapshot = await emailsQuery.get()
const emailEntries = querySnapshot
.map(data=> ({id: data.id, data: data.data()}))
.filter(o=> o.data)
// invoking an async fn -> promise; map returns the result of all invoked fns -> array of promises
const promisedItems = emailEntries.map(async ({id, data: {email}}) => {
const db2 = db.collection("email").doc(id);
const itemsQuery = db2.collection("list").where("expiaryDate", "<", addedDate.getTime())
const items = await itemsQuery.get()
const food = items.filter(o=> o).map(o=> o.data()).filter(o=> o)
.forEach(({name, expiaryDate: time})=> ({name, time}))
return {email, food}
});
const items = await Promise.all(promisedItems)
return items.filter(item=> item.food.length)
}
const add = async ()=> {
let response = await test();
return db.collection("sendGrid").add({response})
.then((item) => console.log('item:', item))
.catch(console.error)
}
Now, the flow is clear!
.
Even more concise (though lack of var names -> less clear) - just for kicks:
// (spelled-fixed expiryDate); down to 23% loc, 42% char
const getUnexpiredFoodPerEmails = async ({expiryDateMax} = {
expiryDateMax: new Date(new Date().addDays(7)),
})=> (await Promise.all((await db.collection('email').get())
.map(data=> ({id: data.id, data: data.data()})).filter(o=> o.data)
.map(async ({id, data: {email}})=> ({
email,
food: (await db.collection('email').doc(id).collection('list')
.where('expiryDate', '<', expiryDateMax.getTime()).get())
.filter(o=> o).map(o=> o.data()).filter(o=> o)
.forEach(({name, expiryDate: time})=> ({name, time})),
}))
)).filter(item=> item.food.length)
const add = async ()=> db.collection('sendGrid').add({
response: await getUnexpiredFoodPerEmails(),
}).then(console.log).catch(console.error)
// ...or with names
const getUnexpiredFoodListForEmailId = async ({id, expiryDateMax} = {
expiryDateMax: new Date(new Date().addDays(7)),
})=> (await db.collection('email').doc(id).collection('list')
.where('expiryDate', '<', expiryDateMax.getTime()).get())
.filter(o=> o).map(o=> o.data()).filter(o=> o)
.forEach(({name, expiryDate})=> ({name, time: expiryDate}))
const getEmails = async ()=> (await db.collection('email').get())
.map(data=> ({id: data.id, data: data.data()})).filter(o=> o.data)
const getUnexpiredFoodPerEmails = async ({expiryDateMax} = {
})=> (await Promise.all((await getEmails()).map(async ({id, data})=> ({
email: data.email,
food: await getUnexpiredFoodListForEmailId({id, expiryDateMax}),
})))).filter(item=> item.food.length)
const add = async ()=> db.collection('sendGrid').add({
response: await getUnexpiredFoodPerEmails(),
}).then(console.log).catch(console.error)

Categories