How to excecute asyncronous promise used Promise.All()? - javascript

I want to ask, how do you run ascyronus promises in the promise all? when I run the function that I made Promise.all runs all the functions in parallel (syncronus)
if you run the code below then the result is
// jon
// andrey
// tania
// JON
// ANDREY
// TANIA
here is my code
// First promise returns an array
const getUsers = () => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve([{ id: 'jon' }, { id: 'andrey' }, { id: 'tania' }]), 600)
})
}
// Second promise relies on the resulting array of first promise
const getIdFromUser = users => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve(users.id), 500)
})
}
// Third promise relies on the result of the second promise
const capitalizeIds = id => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve(id.toUpperCase()), 200)
})
}
const newPromise = async user => {
const userId = await getIdFromUser(user)
console.log(userId) //jon
const capitalizedId = await capitalizeIds(userId)
console.log(capitalizedId) //JON
return
}
const runAsyncFunctions = async () => {
const users = await getUsers()
await Promise.all(users.map(user => newPromise(user)))
}
runAsyncFunctions()
but I want the results as below, this will work if the promise all is run asyncronus
// jon
// JON
// andrey
// ANDREY
// tania
// TANIA
and this is my playground code playground

If you want to initialize a Promise only after the last Promise has finished - that is, run them in serial, not in parallel - then .map and Promise.all is not the right tool for the job, because that'll initialize all Promises at once. await in a for loop instead:
const runAsyncFunctions = async() => {
const users = await getUsers()
for (const user of users) {
await newPromise(user);
}
}
// First promise returns an array
const getUsers = () => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve([{
id: 'jon'
}, {
id: 'andrey'
}, {
id: 'tania'
}]), 600)
})
}
// Second promise relies on the resulting array of first promise
const getIdFromUser = users => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve(users.id), 500)
})
}
// Third promise relies on the result of the second promise
const capitalizeIds = id => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve(id.toUpperCase()), 200)
})
}
const newPromise = async user => {
const userId = await getIdFromUser(user)
console.log(userId) //jon
const capitalizedId = await capitalizeIds(userId)
console.log(capitalizedId) //JON
return
}
const runAsyncFunctions = async() => {
const users = await getUsers()
for (const user of users) {
await newPromise(user);
}
}
runAsyncFunctions()
If your real code allows you to do the logging outside, you can keep running the Promises in parallel, but only log the results in order once they're all completed:
const newPromise = async user => {
const userId = await getIdFromUser(user)
const capitalizedId = await capitalizeIds(userId)
return [userId, capitalizedId];
}
const runAsyncFunctions = async() => {
const users = await getUsers()
const names = await Promise.all(users.map(user => newPromise(user)));
for (const name of names.flat()) {
console.log(name);
}
}
// First promise returns an array
const getUsers = () => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve([{
id: 'jon'
}, {
id: 'andrey'
}, {
id: 'tania'
}]), 600)
})
}
// Second promise relies on the resulting array of first promise
const getIdFromUser = users => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve(users.id), 500)
})
}
// Third promise relies on the result of the second promise
const capitalizeIds = id => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve(id.toUpperCase()), 200)
})
}
const newPromise = async user => {
const userId = await getIdFromUser(user)
const capitalizedId = await capitalizeIds(userId)
return [userId, capitalizedId];
}
const runAsyncFunctions = async() => {
const users = await getUsers()
const names = await Promise.all(users.map(user => newPromise(user)));
for (const name of names.flat()) {
console.log(name);
}
}
runAsyncFunctions()

Promise all runs like a funny wrapper around a normal array interator function...
let results = Promise.all(myarray.map(item => {
try {
return await functionThatReturnsAPromise()
} catch (error) {
console.log(error)
return null
}
}))
A good alternative to Promise all is for of because you can put await on it...
for await (let item of myarray) {
// Try catch etc
}
Node will be fine with this, but you might want to check browser support if you're running it in the front-end.

the getIdFromUser is executed async. This means on the next line, the whole array is already there and that is why it is printed in a serial manner. Fetching both the userId and the capitalized firstly will result in what you want
// First promise returns an array
const getUsers = () => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve([{ id: 'jon' }, { id: 'andrey' }, { id: 'tania' }]), 600)
})
}
// Second promise relies on the resulting array of first promise
const getIdFromUser = async users => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve(users.id), 0)
})
}
// Third promise relies on the result of the second promise
const capitalizeIds = id => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve(id.toUpperCase()), 0)
})
}
const newPromise = async user => {
const userId = await getIdFromUser(user)
const capitalizedId = await capitalizeIds(userId)
console.log(userId) //jon
console.log(capitalizedId) //JON
return
}
const runAsyncFunctions = async () => {
const users = await getUsers()
console.log(users)
await Promise.all(users.map(user => newPromise(user)))
}
runAsyncFunctions()

I have tried to make it simple and removed functions that are not required, I can prodcue the requested output in question
You can alos check here solution
// First promise returns an array
const getUsers = () => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve([{ id: 'jon' }, { id: 'andrey' }, { id: 'tania' }]), 600)
})
}
// Second promise relies on the resulting array of first promise
const getIdFromUser = users => {
return new Promise((resolve, reject) => {
return setTimeout(() => resolve(users.id), 500)
})
}
// You do not need this, why make it complicated ??
// Third promise relies on the result of the second promise
// const capitalizeIds = async id => {
// return new Promise((resolve, reject) => {
// return setTimeout(() => resolve(id.toUpperCase()), 200)
// })
// }
function capitalizeIds(id)
{
return id.toUpperCase();
}
const runAsyncFunctions = async () => {
const users = await getUsers()
users.map(user => {
console.log(user.id) //jon
const capitalizedId = capitalizeIds(user.id);
console.log(capitalizedId) //JON
})
}
runAsyncFunctions()

Related

Chaining three different async functions

I have three async functions but the third function is running before the second one resolves. What is wrong here?
async function getSet () {
//get settings from asyncstorage and setstate
}
async function pairs () {
//fetch data and set another state
}
async function fetchIt () {
//fetch another data and set a third state
}
useEffect(() => {
getSet()
.then(pairs())
.then(fetchIt())
.then(() => {
setLoading(false);
});
}, []);
fetchIt() is running before pairs()
The calls aren't chained properly. To make it simpler use async await:
useEffect(() => {
(async function () {
await getSet();
await pairs();
await fetchIt();
setLoading(false);
})();
}, []);
If each call depends on the result of the last it looks like
const r1 = await getSet();
const r2 = await pairs(r1);
// etcetera
You haven't chained the 'then's properly. They must return a promise as well. Read more here.
const getSet = async() => {
//get settings from asyncstorage and setstate
return new Promise((resolve, reject) => {
resolve('getSet');
});
}
const pairs = async() => {
//fetch data and set another state
return new Promise((resolve, reject) => {
resolve('pairs');
});
}
const fetchIt = async() => {
//fetch another data and set a third state
return new Promise((resolve, reject) => {
resolve('fetchIt');
});
}
getSet()
.then(response => {
console.log(response);
return pairs();
})
.then(response => {
console.log(response);
return fetchIt();
})
.then(response => {
console.log(response);
// setLoading(false);
});

How to make an api call inside of a map function with delay between each call on that api?

This second api call inside of the map function needs to be called in a space of time, because this api does not allow multiple calls at the time. So, the map for each item inside of the array will take two seconds to call the api and after it go to the next item.
How can i fix it?
It does not return anything.
async function HandleMatchList(){
try{
const responseMatches = await api.get('MatchListRankedGames', {
params: {
nickname
}
})
const matches = responseMatches.data
const Awaitfor2seconds = (x) => {
return new Promise (resolve => {
setTimeout(() => {
resolve(x)
}, 5000)
})
}
const linking = async (matches) => {
matches.map(async item => {
const details = await Awaitfor2seconds(
api.get('MatchDetailRoute', {
params: {
gameId: item.gameId,
nickname: nickname
}
}).then(({data}) => {
data
})
)
return details
})
}
linking(matches).then(results => {
setMatches(results)
})
}catch(e){
setError(e)
}
}
You can follow this concept (no tested):
const matches = responseMatches.data
var count = 0 // create a counter
const Awaitfor2seconds = (x) => {
return new Promise (resolve => {
count++ // count++ is the same thing that: count = count + 1
setTimeout(() => {
resolve(x)
}, 5000*count) // using this the request will be send like a queue
})
}
I suggest you make a sleep function separate and then you call it whenever you want to pause your API call
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
try{
const responseMatches = await api.get('MatchListRankedGames', {
params: {
nickname
}
})
const matches = responseMatches.data
await sleep(5000)
const linking = async (matches) => {
results=[]
for(let item of matches){
var details= await api.get('MatchDetailRoute', {
params: {
gameId: item.gameId,
nickname: nickname
}
})
results.push(details)
await sleep(5000)
}
return results
}
linking(matches).then(results => {
setMatches(results)
})
}catch(e){
setError(e)
}

Async module returning promise [object Promise]

I am trying to export the value with instrument variable. however data is returning as [object Promise] than object. How can I assign module variable with the final result rather than the promise object.
var instruments = {
data: async () => {
return new Promise((resolve, reject) => {
/// Respond after retrieving the data
resolve({result : "...." }
);
}
}
var symbols = async () => {
const res = await instruments.data();
return res;
}
module.exports.instrument = symbols().then((data) => {
console.log('data');
return data;
}).catch((e) => {
console.log('error');
return {}
});
It looks like you want a singleton cache. Here is a basic implementation
cache.js
let data = {}
module.exports = {
getData: () => {
return data
},
setData: newData => {
data = newData
return
},
}
No need for async here. I would separate this code with the code that retrieves data.
fetchData.js
const cache = require('./cache')
const fetchData = () => {} // fetch data code here
fetchData().then(data => {
cache.setData(data)
})
try this
var instruments = {
data: () => {
return new Promise((resolve, reject) => {
/// Respond after retrieving the data
resolve({result : "...." });
}
}
var symbols = async () => {
const res = await instruments.data();
return res;
}
module.exports.instrument = symbols;
then import instrument method to call and then call
const instrument = require("./filepath");
instrument().then((data) => {
console.log('data');
}).catch((e) => {
console.log(e);
});
If your async function instruments.data() called, it'll await return Promise.
just append await at return for your expected result.
var instruments = {
data: async () => {
return await new Promise((resolve, reject) => {
// Respond after retrieving the data
resolve({result : "...." });
}
}
or remove async. it's same as above.
var instruments = {
data: () => {
return new Promise((resolve, reject) => {
// Respond after retrieving the data
resolve({result : "...." });
}
}

ExpressJs wait till MongoDB fetch data and loop through before the output

I'm having trouble in figuring this to work, I have one table from MongoDB (collection) for comments and another collection for Users.
When the page load it looks up the comment collection and selects the relevant comments, and then it searches the user table to find the name of the user who made the comment, the data will be combined and then the response is sent.
However, the output is sent before the data is fetched from the user table and added. How can I fix this, here is my code
var output = []
const Comments = require('comments.js')
const Users = require('users.js')
function delay( ) {
return new Promise(resolve => setTimeout(resolve, 300))
}
async function delayedProcess(item) {
await delay()
Users.findById(item.user, async function(err, result) {
Object.assign(item, {name: result.name})
output.push(item)
})
}
async function processArray(array) {
const promises = array.map(delayedProcess)
await Promise.all(promises)
return res.json({data: output})
}
Comments.find({page_id: id}).sort({post_date:-1}).limit(6).then(function(data) {
processArray(data)
})
You are not returning promise from the delayedProcess function.
There you go :-
const Comments = require('comments.js')
const Users = require('users.js')
const output = []
function delayedProcess(item) {
return new Promise((resolve, reject) => {
Users.findById(item.user, function(err, result) {
if (err) return reject (err);
output.push({ ...item, name: result.name })
return resolve()
})
})
}
async function processArray(array) {
const promises = array.map(async(item) => {
await delayedProcess(item)
})
await Promise.all(promises)
return res.json({ data: output })
}
const data = await Comments.find({ page_id: id }).sort({ post_date: -1 }).limit(6)
processArray(data)
However you will always get the concatenated array. So instead taking it globally, take it as local variable
function delayedProcess(item) {
return new Promise((resolve, reject) => {
Users.findById(item.user, function(err, result) {
if (err) return reject (err);
return resolve({ ...item, name: result.name })
})
})
}
async function processArray(array) {
const output = []
const promises = array.map(async(item) => {
const it = await delayedProcess(item)
output.push(it)
})
await Promise.all(promises)
return res.json({ data: output })
}
const data = await Comments.find({ page_id: id }).sort({ post_date: -1 }).limit(6)
processArray(data)
More simplified :- Since mongodb queries itself returns promise you do not need to use new Promise syntax.
async function processArray() {
const array = await Comments.find({ page_id: id }).sort({ post_date: -1 }).limit(6)
const output = []
const promises = array.map(async(item) => {
const it = await Users.findById(item.user).lean()
item.name = it.name
output.push(item)
})
await Promise.all(promises)
return res.json({ data: output })
}

Cannot access object/array in Javascript

I can console.log and see the array I created but as soon as I attempt to access it, I get undefined.
async componentDidMount() {
// fetch goal data for display
let response = await fetchWithToken("http://localhost:8080/api/getGoals");
let goalData = await response.json();
goalData = await goalData.filter(skill => skill.Skill === "CS_en");
// get info from people API with distinct list rather than every row
let people = new Set([]);
goalData
.filter(element => element.UpdatedBy !== null)
.forEach(element => {
people.add(element.UpdatedBy);
});
people = Array.from(people);
// call peopleAPI
const peopleObj = await peopleAPI(people);
console.log("peopleObj :", peopleObj);
console.log("peopleObj[0] :", peopleObj[0]);
}
Here is the peopleAPI where I'm calling another api and getting a list of user info.
const peopleAPI = people => {
return new Promise(function(resolve, reject) {
// get people API info
const peopleObj = [];
const apiPromises = [];
if (people) {
people.forEach(empid => {
const apiPromise = fetch(
`https://someApiCall/${empid}`
)
.then(res => res.json())
.then(res => {
peopleObj.push({
empid: res.id,
name: res.name.preferred ? res.name.preferred : res.name.full
});
})
.then(() => apiPromises.push(apiPromise));
});
// once all promises have been resolved, return a promise with the peopleObj
Promise.all(apiPromises).then(() => {
resolve(peopleObj);
});
}
});
};
export default peopleAPI;
Results of console.logs
Don't use push inside fetch.then, just return its value, and then push it to apiPromises`
const peopleAPI = people => {`
return new Promise(function(resolve, reject) {
// get people API info
const apiPromises = [];
if (people) {
people.forEach(empid => {
const apiPromise = fetch(`https://someApiCall/${empid}`)
.then(res => res.json())
.then(res => {
return {
empid: res.id,
name: res.name.preferred ? res.name.preferred : res.name.full
}
});
apiPromises.push(apiPromise)
});
Promise.all(apiPromises).then((data) => {
resolve(data);
});
}
});
};
export default peopleAPI;
Or even simpler and readable
const peopleAPI = people => {`
const apiPromises = people.map(empid => {
return fetch(`https://someApiCall/${empid}`)
.then(res => res.json())
.then(res => ({
empid: res.id,
name: res.name.preferred ? res.name.preferred : res.name.full
}));
});
return Promise.all(apiPromises)
};

Categories