Why are these promises not resolving? - javascript

Do I need the awaits when inside a promise.all? What else can be wrong with this?
(async => {
const result = Promise.all(
await domainContext.get1(id),
await domainContext.get2(id)
).then(r => r);
})();
I'm expecting :
result = [get1_value, get2_value]
I'm getting :
'{}'

Promise.all expects an array as a single argument, not a list of Promises in its arguments. Also, if you're using Promise.all, don't await inside - that kind of defeats the purpose, because then you're passing an array of resolved values to Promise.all rather than passing it an array of Promises to wait for. Also, to define an async function with no parameters, you need to add an empty parameter list after the async:
const get1 = () => Promise.resolve('get1');
const get2 = () => Promise.resolve('get2');
(async () => {
const result = await Promise.all([
get1(),
get2(),
]);
console.log(result);
})();
You could also await each item in the result array, like this:
const get1 = () => Promise.resolve('get1');
const get2 = () => Promise.resolve('get2');
(async () => {
const result = [
await get1(),
await get2(),
];
console.log(result);
})();
which might have been what you were trying to do, but note that this results in each item being requested in serial, rather than in parallel.

Related

Use fetched API data outside of the function

To make my code cleaner I want to use fetched API data in a few different functions, instead of one big. Even though I 've did manage to reffer to that data in other functions, the problem is the API im a fetching throws different, randomized results every time it is called. And so the output from userData() does not equal that from userData2(), even though my intention is different and I'd like the result variable contents to be the same between functions.
const getData = () =>
fetch("https://opentdb.com/api.php?amount=10").then((response) =>
response.json()
);
const useData = async () => {
const result = await getData();
console.log(result);
};
const useData2 = async () => {
const result = await getData();
console.log(result);
};
Your getData() function returns a promise. One fun fact about promises is that while they can only resolve once, that resolved value can be accessed and used as many times as you want.
const dataPromise = getData();
const useData = async () => {
const result = await dataPromise;
console.log(result);
};
const useData2 = async () => {
const result = await dataPromise;
console.log(result);
};
Using await resolves the promise value, the equivalent of...
dataPromise.then((result) => {
console.log(result);
});
// or `dataPromise.then(console.log)` if you like brevity
I like to point this out about the fetch-api... you should always check the Response.ok property
const getData = async () => {
const res = await fetch("https://opentdb.com/api.php?amount=10");
if (!res.ok) {
throw new Error(`${res.status}: ${await res.text()}`);
}
return res.json();
};

Why does my async function return an empty array?

I'm trying to get the matches of my user by pushing them in an array and returning this array, so my router can send the data to the front-end. But I've got an issue with my async function: I just got an empty array. I've tried to put some breakpoints, and I noticed that my router sends the data before my service pushes the data to the array.
Here is my router code:
router.get("/allMatchs", auth, async (req, res) => {
const user = await userService.getUserById(req);
const matchs = await service.getMatchsByUser(user);
res.send(matchs);
});
and there is my service code:
async function getMatchsByUser(user) {
const userMatchs = user.matchs;
let matchs;
await userMatchs.map(async (m) => {
let match = await Match.findById(m._id).select([
"-isConfirmed",
"-isUnmatched",
]);
matchs.push(match);
});
return matchs;
}
Thank you for your help.
It's because .map() is not async aware. It doesn't wait for the promise that the callback returns. So, when you do this:
await userMatchs.map(...)
The .map() returns an array. You are calling await on an array of promises (remember, .map() returns an array). That doesn't do anything useful. It doesn't wait for anything and the individual iterations inside the .map() didn't wait either.
You can either switch to plain for loop because a forloop is promise aware and it will await properly or you can use await Promise.all(userMatchs.map(...)) .
You could do this:
function getMatchsByUser(user) {
return Promise.all(user.matchs.map((m) => {
return Match.findById(m._id).select([
"-isConfirmed",
"-isUnmatched",
]));
});
}
Or, if you want to do the requests to your database sequentially one at a time, use a plain for loop which await will work in:
async function getMatchsByUser(user) {
let matchs = [];
for (let m of user.matchs) {
let match = await Match.findById(m._id).select([
"-isConfirmed",
"-isUnmatched",
]);
matchs.push(match);
}
return matchs;
}
This code
userMatchs.map(async (m) => {
let match = await Match.findById(m._id).select([
"-isConfirmed",
"-isUnmatched",
]);
matchs.push(match);
}); // -> return an array of promises
It returns an array of promises, so await will not wait for that to execute, -> return empty array.
For this, you should use Promise.all()
Something like this:
async function getMatchsByUser(user) {
const userMatchs = user.matchs;
let matches = await Promise.all(userMatchs.map(async (m) => {
return Match.findById(m._id).select([
"-isConfirmed",
"-isUnmatched",
]);
}));
return matches;
}
Your problem is userMatchs.map
Using async/await combined with map() can be a little tricky
How to use Async and Await with Array.prototype.map()

React Multiple Fetch Requests

I am making 2 fetch requests. I want to convert the data to JSON and then set it to a variable.
async componentDidMount() {
Promise.all([
await fetch('/link1'),
await fetch('/link2'),
]).then(links => {
const response1 = links[0]
const response2 = links[1]
const res1 = response1.json()
const res2 = response2.json()
})
const data1 = res1.data
const data2 = res2.data
...
I'm not able to set data1 and data2 to the responses. I am new to this so I'm not sure how to format it. How do I set data1 and data2?
You can just do
let data = [];
Promise.all([fetch("/link1"), fetch("/link2")])
.then(responses =>
responses.forEach(res => res.json().then(body => data.push(body)))
)
.catch(err => console.error(err));
If you need access to those variables under render, it is common to store them in a state variable.
async componentDidMount() {
Promise.all([
await fetch('/link1'),
await fetch('/link2'),
]).then(links => {
const response1 = links[0]
const response2 = links[1]
const res1 = response1.json()
const res2 = response2.json()
this.setState({data1: res1.data, data2: res2.data})
})
Then use you can use data1 and data2 as this.state.data1/2
To understand why what you are doing is not working, you need to read more about how Promise works. (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
The main reason is because JS is async, so the data1 and data2 are assigned at the same time as Promise.all is fired.
Here's how I would do multiple fetches.
Promise.all(
[
'https://jsonplaceholder.typicode.com/todos/1',
'https://jsonplaceholder.typicode.com/todos/2'
].map(url => fetch(url).then(r => r.json()))
).then(results => console.log(results))
Promise.all takes an array of promises and returns a new promise that is complete when all the provided promises have completed.
It's best to do the .json() work in the thenable chain (since the conversion to JSON is asynchronous as well as the fetch). And then just await the two final results.
The result of the Promise.all is an array of the results of each promise (in the same order you provided the promises to Promise.all -- doesn't matter which order they completed in.)
Don't use await and Promise both. You can try like this to get the expected output.
async componentDidMount() {
const link1Fetch = await fetch('/link1');
const link2Fetch = await fetch('/link2');
const data1 = await link1Fetch.json();
const data2 = await link2Fetch.json();
console.log(data1, data2);
}

Which is better Promise.all or nested async await?

I have two blocks of code. First is using async await
async sendEmailNotifications() {
try {
const users = await User.find(...)
const promises = users.map(async(user) => {
const _promises = user.appId.map(async(app) => {
const todayVisitorsCount = await Session.count({...})
const yesterdayVisitorsCount = await UserSession.count({...})
const emailObj = {
todayVisitorsCount,
yesterdayVisitorsCount
}
const sendNotification = await emailService.analyticsNotification(emailObj)
})
await Promise.all(_promises)
})
return promises
} catch (err) {
return err
}
}
(await sendEmailNotifications())
And then I have using Promise.all
sendEmailNotifications() {
const users = await User.find(...)
const promises = users.map((user) => {
const allPromises = []
user.appId.map((app) => {
allPromises.push(UserSession.count({...}))
allPromises.push(Session.count({...}))
})
const data = await Promise.all(allPromises)
const emailObj = {
todayVisitorsCount: data[0],
yesterdayVisitorsCount: data[1]
}
const sendNotification = await emailService.analyticsNotification(emailObj)
})
return promises
}
sendNotification.then((data) => console.log(data))
Now I need to know which piece of code will faster execute? One is with series(async await) and one is with parellel(Promise.all). Which has better performance?
In the first code, you have two separate await statements:
const todayVisitorsCount = await Session.count({...})
const yesterdayVisitorsCount = await UserSession.count({...})
whereas in the second, you only have one, before a Promise.all:
const data = await Promise.all(allPromises)
In the first code, the second Promise will only initialize after the first Promise has finished, resulting in a longer time required before the script ends. For example:
const fn = () => new Promise(resolve => setTimeout(resolve, 1000));
console.log('start');
(async () => {
await fn();
await fn();
console.log('two awaits done');
})();
(async () => {
await Promise.all([fn(), fn()]);
console.log('Promise.all done');
})();
The version without Promise.all pauses the function when the first call of fn() is made, and waits for the Promise returned by fn() to resolve (1000 ms) before proceeding to the next line. The next line calls fn() again, and the await waits for it to complete (1000 more ms).
In contrast, the Promise.all version calls both fn()s immediately - both Promises are initialized, and the await that pauses the function is waiting for both Promises to complete. There's no down time between the initialization of the first Promise and the initialization of the second Promise.
So, the Promise.all version will run more significantly more quickly than the version with two awaits. Using Promise.all will be preferable unless the first Promise (UserSession.count) must be completed before the second Promise (Session.count) starts.
With destructuring and without unnecessary variables, this is how I would clean up your Promise.all code, you might consider it to be a bit more readable:
async sendEmailNotifications() {
const users = await User.find();
return users.map(async (user) => {
const [todayVisitorsCount, yesterdayVisitorsCount] = await Promise.all([
UserSession.count(),
Session.count()
]);
await emailService.analyticsNotification({ todayVisitorsCount, yesterdayVisitorsCount });
});
}

Assign variable from fetch returning Promise

I have two files that are arrays, and i want to load them from a fetch. I have an async function that fetch the files:
async function getData(file) {
const data = await fetch(`./assets/resources/${file}.json`);
return await data.json()
}
Then here is where i assign the variables to the return fo this fetch:
let notes = getData("notes").then(res => res)
let pentagrama = getData("pentagrama").then(res => res)
But with this all i get is:
from google chrome console
How can i actually get the value?
The result of getData is always a Promise that resolves to your data. To access the values, you can use async/await:
(async () => {
let notes = await getData("notes");
let pentagrama = await getData("pentagrama");
// use them here
})();
Alternatively, you can use Promise.all to wait for both promises to resolve, and then access the received data:
let notesP = getData("notes");
let pentagramaP = getData("pentagrama");
Promise.all([noteP, pentagramaP]).then(res => {
let notes = res[0];
let pentagrama = res[1];
// use them here
});
ASYNC
AWAIT
This will work for you if you just want to check the response in your Google Chrome console because in the console you can use await without an async function which probably could be because everything executed in the console is wrapped in an async function by default(just a speculation).
ONLY WORKS IN CONSOLE:
const getData = (file) => (
fetch(`./assets/resources/${file}.json`).then(data => data.json());
)
let notes = await getData("notes")
let pentagrama = await getData("pentagrama")
But if you want to get this working in an application, remember that you ALWAYS need to wrap an await inside async
TO GET IT WORKING IN AN APPLICATION:
const getData = async (file) => (
await fetch(`./assets/resources/${file}.json`).then(data => data.json());
)
const wrapperFunc = async () => {
let notes = await getData("notes")
let pentagrama = await getData("pentagrama")
}

Categories

Resources