I Have 2 functions one which uses async and await to grab data and place it into an array.
The second is acts like a checker to see if the user inputs a similar value as seen on the database
function repeatsChecker() {
let api_data_values = []
fetchData().then(data => {
for (let i = 0; i < data.length; i++) {
api_data_values.push(data[i].name)
}
})
return api_data_values
}
// testing for similarities
async function test() {
let name = "Joe"
let test = await repeatsChecker();
console.log(test[0])
}
test()
When I compile a simple if statement everything returns true and when I do console.log(test[0])
it returns undefined?
repeatChecker isn't returning a promise, so the fact that you're awaiting doesn't have any meaningful effect. console.log(test[0]) executes before api_data_values.push(data[i].name).
Try this:
function repeatsChecker() {
return fetchData().then(data => {
return data.map(value => value.name);
});
}
Or with async/await:
async function repeatsChecker() {
const data = await fetchData();
return data.map(value => value.name);
}
Related
Suppose I have code like this:
let result = {
name: downloadNameFromInternetVerySlow(),
isFamous: determineIfNameIsFamous(this.name);
}
const downloadNameFromInternetVerySlow = () => {
path = "/home";
const folders = fs.readdirSync(path).filter(file => fs.lstatSync(path).isDirectory());
console.log(folders);
return folders[0];
}
downloadNameFromInternetVerySlow can take a long time, meanwhile determineIfNameIsFamous depends on downloadNameFromInternetVerySlow's result to return correct value.
How do I make sure determineIfNameIsFamous only runs after downloadNameFromInternetVerySlow is done?
The code you show is entirely synchronous so asynchronous results aren't actually the issue here like people were guessing. The issue is that this.name cannot be used to refer to a prior property in an object literal definition for several reasons, not the least of which this isn't set to the object you want.
Instead, you can do this:
let result = {};
result.name = downloadNameFromInternetVerySlow();
result.isFamous = determineIfNameIsFamous(result.name);
You can convert downloadNameFromInternetVerySlow to a Promise(if not already). and then user await to wait till it finished. Otherwise you can use Promise.then()
(This should be inside an async function)
let name = await downloadNameFromInternetVerySlow(),
let isFamous = determineIfNameIsFamous(this.name);
let result = {
name,
isFamous
}
Another method using Promise.then
let name, isFamous;
downloadNameFromInternetVerySlow().then(result => {
name = result;
isFamous = determineIfNameIsFamous(this.name);
});
Async/await
There’s a special syntax to work with promises in a more comfortable fashion, called “async/await”.
An async function returns a promise, like in this example:
const doSomethingAsync = () => {
return new Promise(resolve => {
setTimeout(() => resolve('I did something'), 3000)
})
}
When you want to call this function you prepend await, and the calling code will stop until the promise is resolved or rejected. One caveat: the client function must be defined as async. Here's an example :
const doSomething = async () => {
console.log(await doSomethingAsync())
}
function downloadNameFromInternetVerySlow(){
return new Promise(resolve => setTimeout(() => {
console.log("Set timeout resolving...");
return resolve("Sample Name");
}, 5000))
}
function determineIfNameIsFamous(name){
if(name === "Sample Name"){
return true;
}
return false;
}
async function myFunc(){
let result = {};
result.name = await downloadNameFromInternetVerySlow();
console.log(result);
result.isFamous = determineIfNameIsFamous(result.name);
return result;
}
myFunc().then(res => console.log("result : ", res))
Assuming downloadNameFromInternetVerySlow is asynchrononous (otherwise it would already work like you want), the solution is to use await.
Please note, to be able to do that, this code needs to be wrapped in an async function (or or top level with top level await available.)
async function getAndUseResult(){
let result = {
name: await downloadNameFromInternetVerySlow(),
isFamous: determineIfNameIsFamous(this.name)
}
console.log(JSON.stringify(result))
}
getAndUseResult();
I am trying to loop through records in a database, in order to compile an array (cardsToInsert) that I will write to another database.
I was getting stuck because the array was writing to the database before the loop finished, I know I need to use promises / async functions to achieve what I want, but I'm pretty sure I'm doing something wrong with my promises.
The code works for a few loops (it goes for about 6-10 loops, it's supposed to loop 16 times), but then hangs while trying during wixData.get (or it hangs on a different promise that is part of buildCard).
// wixData.get is a function that returns a promise
async function loopCards(cardsToGet) {
let writeCard
let buildCard
for (let index = 0; index < cardsToGet.length; index++) {
const cardToGet = cardsToGet[index].card
buildCard = await wixData.get("Card", cardToGet)
.then((card) => {
return card
})
.catch((err) => {
let errorMsg = err;
return errorMsg
});
writeCard = await buildingCard(buildCard)
cardsToInsert.push(writeCard)
}
return cardsToInsert
}
What am I doing wrong? (or what is the key thing I'm doing wrong that is stopping this working, I'm sure there is plenty to be improved here!)
UPDATE
I've now updated the code and it loops through fine.
async function loopCards(cardsToGet) {
console.log('Start')
let writeCard
let buildCard
for (let index = 0; index < cardsToGet.length; index++) {
const cardToGet = cardsToGet[index].card
buildCard = wixData.get("Card", cardToGet)
.then(async (card) => {
writeCard = await buildingCard(card)
cardsToInsert.push(writeCard)
})
.catch((err) => {
let errorMsg = err;
return errorMsg
});
}
return cardsToInsert
}
How do I get it to wait for the loop to finish before finally returning cardsToInsert?
Your mix of async/await and .then is not really best practice
This should work, and will return once cardsToInsert is populated
async function loopCards(cardsToGet) {
const cardsToInsert = [];
for (let cardToGet of cardsToGet) {
try {
const card = await wixData.get("Card", cardToGet);
const writeCard = await buildingCard(card);
cardsToInsert.push(writeCard);
}
catch(err) {
let errorMsg = err;
return errorMsg;
}
}
return cardsToInsert;
}
better still, you really don't need to handle any errors here, since the calling function could do that
So it becomes even simpler
async function loopCards(cardsToGet) {
const cardsToInsert = [];
for (let cardToGet of cardsToGet) {
const card = await wixData.get("Card", cardToGet);
const writeCard = await buildingCard(card);
cardsToInsert.push(writeCard);
}
return cardsToInsert;
}
then using it could be like
loopCards(cards)
.then(result => doSomethingWihtResult)
.catch(error => handleError);
or if calling from an async function
try {
let result = await loopCards(cards);
// do something with result
} catch(error) {
// handle Error
}
The code snippet below is not running in the proper order... the for loop is calling the downloadFile function call over every iteration before any other functions can resolve, which is affecting the number of links returned to the final callback.
What I need is an loop iteration to finish only once the appendLinksList function has resolved and returned a link. I'm assuming I need async to block the functions? How would I use async to get the proper flow instead of the downloadFiles function being called before anything else can resolve a value?
Note that the callback functions have been defined above this code snippet
const entries [ /* Array of objects containing a path_display property */];
let links = [];
for(let i = 0; i < entries.length; i++) {
const appendLinksList = link => {
if(i !== entries.length - 1) return links = [...links, link];
return callback(null, links);
};
const downloadFile = path => {
Dropbox.filesGetTemporaryLink({ path })
.then(file => {
return appendLinksList(file.link);
})
.catch(err => {
return res.status(500).json(err.message);
});
};
downloadFile(entries[i].path_display);
};
You can wrap your loop in an immediately invoked async function.
const linksResolved = await (async() => {
let links = [];
for(let i = 0; i < entries.length; i++) {
const appendLinksList = link => {
if(i !== entries.length - 1) return links = [...links, link];
return callback(null, links);
};
const path = entries[i].path_display;
await Dropbox.filesGetTemporaryLink({ path })
.then(file => {
return appendLinksList(file.link);
})
.catch(err => {
return res.status(500).json(err.message);
});
};
};
return links;
})()
However, the result you want (links) will forever be a promise.
Can only be resolved in an async function. Or you can call a callback with links from inside the immediatelyInvokedAsyncFunction
I'm having difficulty accessing the values from getPeople(0,4).
function getPeople(start, end) {
const peopleArray = [];
for (let i = start; i <= end; i++) {
peopleArray.push(
axios.get(`https://www.testsite.net/api/test/workers/${i}`)
);
}
return peopleArray;
}
useEffect(() => {
Promise.all([getData(), getPeople(0, 4)]).then(item => {
//console.log(item[0].data.orders); //
setData(item);
setPersonData(item);
});
}, []);
item[0] works fine.
Here's the result I'm getting when I console.log(item[1]) How can I access the data?
item[1] is an array of promises.
You simply need to spread the array returned from getPeople() like so:
Promise.all([getData(), ...getPeople(0, 4)]).then(item => {
console.log(item);
});
Promise.all() expects an array of Promise, you were passing an array containing another array.
The getPeople function returns an array of promises.
If you want to await those promises in the Promise.all call, one option is to:
useEffect(() => {
Promise.all([getData(), ...getPeople(0, 4)]).then(item => {
//console.log(item[0].data.orders);
console.log(item[1]); // this will effectively output the people 0 object
setData(item);
setPersonData(item);
});
}, []);
The above will receive as item[0] the resolved value from the promise of getData (which sounds expected already). Then item[1] through item[5] will be the 5 people objects you seem to be expecting.
This is can be made more readable using an async function in useEffect
//make sure this actually returns the people rather than a load of promises
async function getPeople(start, end) {
const peopleArray = [];
for (let i = start; i <= end; i++) {
let person = await axios.get(`https://www.hatchways.io/api/assessment/workers/${i}`);
peopleArray.push(person);
}
return peopleArray;
}
//then in useEffect you need to create another async function
useEffect(() => {
async function getDataAndPersons() {
const data = await getData();
setData(data);
const people = await getPeople(0, 4);
people.forEach(person => setPersonData(person));
};
getDataAndPersons();
}, []);
Below is my function
async function test() {
const data= [];
for (let i = 0; i <= 5; i = i + 1) {
const payload: any = await postExampleApi(i);
data.push(payload);
}
return data;
}
export const getData = test().then((data) => {
console.log(data);
return data;
});
I try to call api six times with different params(number 0 - 5) each times.
I can have my data array from console.log, however, when I import this getData function from another file, I always get a Promise{...} not data array.
Did I do something wrong?
Whenever you do this :
let x = promise().then(cb)
You get "x" as promise only. All you should do is when requiring this file, do a "dot then" to get the data.
So wherever you require the file, do this :
let test = require('./test');
test.then((dataArray) => console.log(data));
Because you are calling a async function getData === async test(), if you don't want to deal with the then, you can make your call like await getData()