I have a simple function that calls a firestore collection and returns a list of companies. I iterate through the list and output the company name. Once it's complete, I want to write "done".
function getCompanies() {
firestore.collection('companies')
.listDocuments()
.then(companies => {
for(var i in companies) {
companies[i].get().then(company => {
console.log(company.name);
});
}
}).catch(err => {
console.error(err);
});
};
getCompanies();
console.log('done');
In reality, this is what happens...
done
ford
gm
toyota
jeep
vw
I've looked into promises...
function getCompanies() {
firestore.collection('companies')
.listDocuments()
.then(companies => {
let promises = [];
for(var i in companies) {
companies[i].get().then(company => {
promises.push(doIt(company.id));
});
}
Promise.all(promises)
.then((results) => {
console.log("All done", results);
})
.catch((e) => {
// Handle errors here
});
}).catch(err => {
console.error(err);
});
};
function doIt(value) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(value);
resolve(value);
}, Math.floor(Math.random() * 1000));
});
}
getCompanies();
But that didn't work either...
All done []
ford
gm
toyota
jeep
vw
Any pointers or advice? I feel like I'm forgetting to do something obvious :/.
Thanks in advance!
That is because the console.log is synchrnous and getCompanies() is asynchrnous. The getCompanies will resolve in the future. Read up on Promise and async/await a bit.
A quick fix without using async/await will be:
function getCompanies() {
return firestore.collection('companies')
.listDocuments()
.then(companies => {
const promises = [];
for(var i in companies) {
promises.push(companies[i].get());
}
return Promise.all(promises);
}).catch(err => {
console.error(err);
});
};
Now to run the getCompanies and console.log in order, you need to execute the console.log after getCompanies resolve
getCompanies()
.then((companiesarr)=>{
companiesarr.forEach((c)=>{
console.log(c.name)
})
}).then(()=>console.log('done'))
You need to return the proimse from your function like this
function getCompanies() {
return firestore.collection('companies')
.listDocuments()
.then(companies => {
for(var i in companies) {
companies[i].get().then(company => {
console.log(company.name);
});
}
}).catch(err => {
console.error(err);
});
};
This is a great overview of promises which explains which blocks of code are asynchronous.
https://levelup.gitconnected.com/async-await-vs-promises-4fe98d11038f
If you want 'done' to print in the correct order it must be called within the async block.
If I understand correctly, .get() returns a promise so you would need to do something like this.
function getCompanies() {
firestore
.collection('companies')
.listDocuments()
.then(companies => {
const myPromisesArr = companies.map(i => i.get());
Promises.all(myPromisesArr).then(companies => {
companies.forEach(company => {
console.log(company.id);
});
});
})
.catch(error => console.error(error))
.finally(() => console.log(`done`));
}
Related
I have two api call.
I want to do some calculation based on the results of both api.
I am using Promise.all() for waiting for both promises to resolve.
const getHashTagList = async () => {
loader.start();
try {
await getAllHashTags().then((response: any) => {
setHashtagList([...response?.data]);
});
} catch (err) {
} finally {
loader.stop();
}
};
const getUserFollowingHT = async () => {
loader.start();
try {
await getUserDetails().then((response: any) => {
setUserFollowingHT([...response?.data?.followingHashtags]);
});
} catch (err) {
} finally {
loader.stop();
}
};
For calling these 2 promises I am using below syntax:
useEffect(() => {
//getHashTagList();
// getUserFollowingHT();
Promise.all([getHashTagList, getUserFollowingHT]).then(
(combineResp) => {
console.log(combineResp);
}
);
}, []);
But in the output I am getting function declaration syntax.
It is not able to get call those promises.
Try this
useEffect(() => {
(async () => {
const values = await Promise.all([getHashTagList, getUserFollowingHT]);
console.log(values);
})();
}, []);
im trying to write a promise but seems to be missing something. here is my code:
const myPromise = new Promise(() => {
setTimeout(() => {
console.log("getting here");
return setinputs({ ...inputs, images: imageAsUrl });
}, 100);
});
myPromise
.then(() => {
console.log("getting here too");
firebase.database().ref(`collection/${idNode}`).set(inputs);
})
.then(() => {
console.log("all is set");
})
.catch((err) => {
console.log(err);
});
if i run the program, the first part of the promise is executing but all .then() functions arent executing. how do i fix this?
In this scheme, the promise callback has one (resolve) or two (resolve,reject) arguments.
let p = new Promise((resolve, reject)=> {
//do something
//resolve the promise:
if (result === "ok") {
resolve(3);
}
else {
reject("Something is wrong");
}
});
p.then(res => {
console.log(res); // "3"
}).catch(err => {
console.error(err); //"Something is wrrong
});
Of course, nowadays you can use async + await in a lot of cases.
You need to resolve the promise, using resolve() and also return the promise from firebase so the next .then in the chain works properly.
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("getting here");
// You have to call resolve for all `.then` methods to be triggered
resolve({ ...inputs, images: imageAsUrl });
}, 100);
});
myPromise
.then((inputs) => {
console.log("getting here too");
// You have to return a promise in a .then function for the next .then to work properly
return firebase.database().ref(`collection/${idNode}`).set(inputs);
})
.then(() => {
console.log("all is set");
})
.catch((err) => {
console.log(err);
});
I have an issue with sending a request to backend from my componentDidMount(). Basically I need to do two things before rendering screen:
Obtain data from API call and save it to state
Send that obtained data to backend and take response values from backend.
The problem I've faced on first step is that setState() is async, and even though my array is not empty (I see it's elements in render() and componentDidUpdate fucntion) in componentDidMount() when I console.log() array it will be empty. Now, the issue is: I still need to send that state array to backend before showing the screen. But how can I do it, when it appears empty there?
I have everything working fine if I send the request from the Button element in my render function, but that's not exactly what I need. Any suggestions?
this.state = {
ActivityItem: [],
}
componentDidMount() {
this.getDataFromKit(INTERVAL); //get data from library that does API calls
this.sendDataToServer(); //sending to backend
}
componentDidUpdate() {
console.log("componentDidUpdate ", this.state.ActivityItem) // here array is not empty
}
getDataFromKit(dateFrom) {
new Promise((resolve) => {
AppleKit.getSamples(dateFrom, (err, results) => {
if (err) {
return resolve([]);
}
const newData = results.map(item => {
return { ...item, name: "ItemAmount" };
});
this.setState({ ActivityItem: [...this.state.ActivityItem, ...newData] })
})
});
And last one:
sendDataToServer() {
UserService.sendActivityData(this.state.ActivityItem).then(response => {
}).catch(error => {
console.log(error.response);
})
And here it works as expected:
<Button
title='send data!'
onPress={() => this.sendDataToServer()
} />
UPDATE
If I have like this (wrapped inside initKit function this will return undefined.
AppleKit.initKit(KitPermissions.uploadBasicKitData(), (err, results) => {
if (err) {
return;
}
return new Promise((resolve) => {
AppleKit.getSamples(dateFrom, (err, results) => {
if (err) return resolve([]);//rest is the same
you have to wait for the promise to resolve. You need something like this:
componentDidMount() {
this.getDataFromKit(INTERVAL).then(result => {
this.sendDataToServer(result); //sending to backend
}).catch(e => console.error);
}
and you can update your other function that fetches data to return it:
getDataFromKit(dateFrom) {
return new Promise((resolve) => {
AppleKit.getSamples(dateFrom, (err, results) => {
if (err) return resolve([]);
const newData = results.map(item => {
return { ...item, name: "ItemAmount" };
});
const allData = [ ...this.state.ActivityItem, ...newData ];
this.setState({ ActivityItem: allData });
resolve(allData);
});
});
}
finally, you need the 'sendData' function to not depend on state, but get a param passed to it instead:
sendDataToServer(data) {
UserService.sendActivityData(data).then(response => {
// ... do response stuff
}).catch(error => {
console.log(error.response);
});
}
Handling Multiple Requests
if the requests don't depend on each other:
componentDidMount() {
Promise.all([
promise1,
promise2,
promise3,
]).then(([ response1, response2, response3 ]) => {
// do stuff with your data
}).catch(e => console.error);
}
if the requests do depend on each other:
componentDidMount() {
let response1;
let response2;
let response3;
promise1().then(r => {
response1 = r;
return promise2(response1);
}).then(r => {
response2 = r;
return promise3(response2);
}).then(r => {
response3 = r;
// do stuff with response1, response2, and response3
}).catch(e => console.error);
}
as far as your update, it seems like you wrapped your async request in another async request. I'd just chain it instead of wrapping it:
make the initKit a function that returns a promise
function initKit() {
return new Promise((resolve, reject) => {
AppleKit.initKit(
KitPermissions.uploadBasicKitData(),
(err, results) => {
if (err) reject({ error: 'InitKit failed' });
else resolve({ data: results });
}
);
});
}
make get samples a separate function that returns a promise
function getSamples() {
return new Promise((resolve) => {
AppleKit.getSamples(dateFrom, (err, results) => {
if (err) resolve([]); //rest is the same
else resolve({ data: results });
});
});
}
chain 2 promises back to back: if initKit fails, it will go in the .catch block and getSamples wont run
componentDidMount() {
initKit().then(kit => {
return getSamples();
}).then(samples => {
// do stuff with samples
}).catch(e => console.log);
}
I have async function in async function. In the second I must wait when promises resolve or reject and after run other code below. But if promise reject my code stopping and no run other functions. How I can fix it?
await axios.all(promises).then(res => {
axios.patch("/url", { foo: bar }).then(async () => {
const promises2 = arr.map(item => {
return axios.post("/url-2", item)
});
await Promise.all(promises2)
.then(() => console.log("resolved")) //this not calling ever
.catch(() => console.log("failed")) //this not calling ever
console.log("This console log ever not working")
})
})
Promises aren't chained properly, axios.patch(...) promise isn't returned. await is syntactic sugar for then and catch, its purpose is to get rid of nested functions where possible. It should be:
const res = await axios.all(promises)
await axios.patch("/url", { foo: bar })
const promises2 = arr.map(item => {
return axios.post("/url-2", item)
});
try {
await Promise.all(promises2)
console.log("resolved"))
} catch (err) {
console.log("failed");
}
The order of your code is wrong. Of course if the first promise is rejected, then the rest will not be called.
Try rewriting your code this way:
let res = await axios.all(promises).catch(() => { console.log("failed"); return false; });
if (!res) {
// Do something when rejected
....
}
// Call the 2nd promise
let res2 = await axios.path("/url", {foo: bar}).catch(() => {console.log("failed 2"); return false; });
if (!res2) {
// Do something when the 2nd promise is rejected
...
}
// Call your last promise
let res3 = await Promise.all(promises2).catch(() => {console.log("failed 3"); return false; });
if (!res3) {
// Do something if it is rejected again
....
}
// Otherwise, do your thing
Try this code, it should pinpoint where the error or rejection is occurring (i.e. it's definitely before Promise.all(promises2) is run
await axios.all(promises)
.then(res => axios.patch("/url", { foo: bar }), err => {
throw `all(promises) failed with ${err}`;
})
.then(() => {
const promises2 = arr.map(item => {
return axios.post("/url-2", item);
});
return Promise.all(promises2)
.then(() => console.log("resolved")) //this not calling ever
.catch(err => {
throw `all(promises2) failed with ${err}`;
});
}, err => {
throw `patch failed with ${err}`;
})
.catch(err => console.error(err));
Note I've removed async/await, because in the code you've posted it is totally unnecessary
My nodejs api function:
exports.userSignup = (req, res) => {
const home = {
address: req.body.name,
phoneno: req.body.code,
};
Home.create(home)
.then((data) => {
createUser()
// i want to complete the above createUser() function fully then only i need to move to this below then function
.then(() => {
const loginDetails = {
username: 'stackoverflow',
};
User.create(loginDetails)
.then((data) => {
return res.status(200).send(data);
}).catch((err) => {
console.log('error while create schema:', err);
});
});
})
.catch((err) => {
console.log('err:', err);
});
};
My createUser function code:
const createUser = () => {
Home.findAll({
raw: true,
}).then((data) => {
data.forEach((client) => {
postgresDB.createSchema(client.code).then(() => {
Object.keys(postgresDB.models).forEach((currentItem) => {
postgresDB.models[currentItem].schema(client.code).sync();
});
console.log('Postgres schema created');
}).catch((err) => {
console.log(err);
});
});
}).catch((err) => {
console.log('Warning:', err.message);
});
};
createUser();
But this one is working as asynchronous,
How to make this using promise resolve reject or callback?
See my code, i made a comment which needs to work first,
I tried with async await but not working!
In order for promise chaining to work, you have to return a promise for any asynchronous functions or else it won't wait. You're also making another asynchronous call for each client you iterate through. In order to deal with multiple promises at once, you need to push each promise into an array and pass it to Promise.all. Here's a snippet that should work.
const createUser = () => {
return Home.findAll({
raw: true,
}).then((data) => {
const promises = [];
data.forEach((client) => {
promises.push(
postgresDB.createSchema(client.code).then(() => {
Object.keys(postgresDB.models).forEach((currentItem) => {
postgresDB.models[currentItem].schema(client.code).sync();
console.log('Postgres schema created');
})
})
);
});
return Promise.all(promises);
}).catch((err) => {
console.log('Warning:', err.message);
});
};
The problem lies in the synchronous data.forEach() call inside the createUser function which doesn't wait for the async createSchema calls to finish before returning.
You can fix this by using an async implementation of forEach function or by using Promise.all()
Here's a piece of code that might work for you:
const createUser = () => {
return Home.findAll({
raw: true
}).then((data) => Promise.all(
data.map((client) => postgresDB.createSchema(client.code).then(() =>
Promise.all(Object.keys(postgresDB.models).map((currentItem) =>
postgresDB.models[currentItem].schema(client.code).sync()
))
))
))
.catch((err) => {
console.log('Warning:', err.message);
});
};
If async/await doesn't quite work for you, just use promise chaining. It's super painful to see people just write callback based code with promises and forget that you can chain promises by returning a promise from the onFulfilled() parameter to then():
const createUser = () => {
return Home.findAll({ raw: true }).
then(data => {
return Promise.all(data.map(client => postgresDB.createSchema(client.code)));
}).
then(() => {
const keys = Object.keys(postgresDBModels);
return Promise.all(keys.map(currentItem => {
return postgresDB.models[currentItem].schema(client.code).sync();
}));
}).
then(() => console.log('Postgres schema created')).
catch((err) => {
console.log(err);
});
};
createUser();
You can use async and await in createUser function. You can alter your function by Using promise, We can able to either resolve or reject.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function