Convert child process based Promise to async await - javascript

This is my existing code. I tried to get data with a child process spawned, and the promise resolves after the child is terminated
const {spawn} = require('child_process')
const getDataFromChildProcess = params => new Promise(resolve => {
const child = spawn('script.js',[params])
let data = null
child.on('data',result => {
data = result
})
child.on('exit',() => {
resolve(data)
})
})
getDataFromChildProcess('foo')
.then(result => {
console.log(result)
})
How do I convert it into async-await style?

await will work inside async function. For your example - you will need to wrap your operation inside a async fn.
With that said you When you wrap something with a fn, you will need to execute that fn too. For that I used IIFE in my example- https://developer.mozilla.org/en-US/docs/Glossary/IIFE
refer - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function#Description
const {spawn} = require('child_process')
const getDataFromChildProcess = params => new Promise(resolve => {
const child = spawn('script.js',[params])
let data = null
child.on('data',result => {
data = result
})
child.on('exit',() => {
resolve(data)
})
})
(async() => {
try{
const result = await getDataFromChildProcess('foo')
console.log(result)
} catch(e) {
console.log(e)
}
})()

Related

Js not recognizing array returned from asynchronous call

const getRecipes = async (flasks) => {
let newList = []
let recipes = flasks[0].recipes;
await Promise.all(recipes.map(async (recipe) => {
const response = await axios.get(`https://us.api.blizzard.com/data/wow/recipe/${recipe.id}?namespace=static-us&locale=en_US&access_token=${api_token}
`)
newList.push(response.data)
}))
// flasks[0].recipes.forEach(recipe => {axios.get(`https://us.api.blizzard.com/data/wow/recipe/${recipe.id}?namespace=static-us&locale=en_US&access_token=${api_token}
// `).then(response =>
// newList.push(response.data)
// )})
console.log(new Date())
console.log('logging newList')
console.log(newList)
return newList;
// setpotions_recipes(newPotions);
}
useEffect(() => {
axios.get(`https://us.api.blizzard.com/data/wow/connected-realm/104/auctions?namespace=dynamic-us&locale=en_US&access_token=${api_token}`).then(
response => {
const auctionList = response.data.auctions;
setAuctions(auctionList);
console.log('fetched auctions');
}
)
main();
}, [])
const main = async () => {
getRecipes(flasks).then( response => {
console.log('response:')
console.log(response)
getIngredientsList(response)
})
}
const getIngredientsList = (recipe_type) => {
let newList = new Map(ingredientsMap); //ingredientsMap is a state that holds information, this function tries to update ingredients (an array version of ingredientsMap)
console.log('In getIngredientsList');
console.log(recipe_type);
console.log(recipe_type.length);
recipe_type.forEach(item => {
console.log('inside recipe_type forEAch')
item.reagents.forEach(reagent => {
newList.set(`${reagent.reagent.name}`, {id: reagent.reagent.id, min: Infinity})
}
)
})
console.log(new Date())
setIngredientsMap(newList);
let newArray = Array.from(ingredientsMap, ([name, {id, min}]) => ({name, id, min}))
console.log(`logging ingredients from ${recipe_type}`)
console.log(newArray);
setIngredients(newArray);
}
Essentially I am trying to grab a set of items from an api, and then parse those items afterwards. I'm new to asynchronous functions so what I tried is to make getRecipes asynchronous and once that returns its list, call getIngredientsList on that response.
However, getIngredientsList logs recipe_type to be of length 0.
I believe this is because my asynchronous calls are not lining up correctly, but don't exactly know how to fix it.

Add await inside .then

I have below code in which i want to call an await after getting response in .then.
const info = new getInfo(this.fetchDetails);
info
.retrieve()
.then((res) => {
const details = this.getLatestInfo(res, 'John');
})
.catch((error) => {
console.log(error);
});
In nutshell want to make const details = this.getAgentInfo(res, 'John'); as const details = await this.getLatestInfo(res, 'John'); but its giving error as "await is only valid in async function", how to make this async?
Mixing then() and its syntactic variant async / await is like speaking with two different accents. Rewriting the code to strictly use each variation, we get:
Older way:
function promiseReturningFunction() {
const info = new getInfo(this.fetchDetails);
return info.retrieve().then((res) => {
return this.getLatestInfo(res, 'John');
}).catch((error) => {
console.log(error);
});
}
// call it
promiseReturningFunction().then(res => {
// res will be the result of getLatestInfo()
}
Newer way:
async function promiseReturningFunction() {
const info = new getInfo(this.fetchDetails);
try {
const res = await info.retrieve();
const info = await this.getLatestInfo(res, 'John');
return info;
} catch (error) {
console.error(error);
}
}
// call it
const res = await promiseReturningFunction(); // res will be the result of getLatestInfo()
You need to use async keyword to make the function async as shown below.
const info = new getInfo(this.fetchDetails);
info
.retrieve()
.then(async (res) => {
const details = await this.getLatestInfo(res, 'John');
})
.catch((error) => {
console.log(error);
});

writeFile does not wait for variable to be instantiated

I'm new to node.js and javascript in general but I am having issues understanding why the writeFile function is writing a blank file. I think the for loop should be a Promise but I am not sure how to write it.
const removeProviders = function () {
readline.question('Where is the file located? ', function(filePath) {
let providerArray = fs.readFileSync(filePath).toString().split('\r\n');
console.log(providerArray);
let importFile = '';
for (let providerId in providerArray) {
getProvider(providerArray[providerId]).then((response) => {
let providerInfo = response.data;
return providerInfo;
}).then((providerInfo) => {
let entry = createImportEntry(providerInfo);
importFile += "entry";
})
}
fs.writeFile('C:/path/to/file.txt', importFile);
You can collect all the promises from the for-loop into an Array and then pass them into Promise.all() which will resolve only after all of them resolved. If one of the promises are rejected, it will reject as well:
const promises = providerArray.map((item) => {
return getProvider(item)
.then((response) => {
let providerInfo = response.data;
return providerInfo;
})
.then((providerInfo) => {
let entry = createImportEntry(providerInfo);
importFile += "entry";
});
});
Promise.all(promises).then(() => {
fs.writeFile('C:/path/to/file.txt', importFile, err => {
if (err) {
console.error(err);
}
});
});
While doing this you could also get rid of the importFile variable and collect directly the results of your promises. Promise.all().then(results => {}) will then give you an array of all results. Thus no need for an updatable variable.
The below approach may be useful.
I don't know your getProvider return Promise. you can set promise in getProvider method
const getProviderValue = async function(providerArray) {
return new Promise((resolve, reject) {
let importFile = '';
for (let providerId in providerArray) {
await getProvider(providerArray[providerId]).then((response) => {
let providerInfo = response.data;
return providerInfo;
}).then((providerInfo) => {
let entry = createImportEntry(providerInfo);
importFile += "entry";
})
}
resolve(importFile)
})
}
const removeProviders = async function () {
readline.question('Where is the file located? ', function(filePath) {
let providerArray = fs.readFileSync(filePath).toString().split('\r\n');
console.log(providerArray);
let importFile = await getProviderValue(providerArray)
fs.writeFile('C:/path/to/file.txt', importFile);
})
}
Your for loop does not wait for the promises to resolve. A better way to approach this problem would be to use reduce.
providerArray.reduce(
(p, _, i) => {
p.then(_ => new Promise(resolve =>
getProvider(providerArray[providerId]).then((response) => {
let providerInfo = response.data;
return providerInfo;
}).then((providerInfo) => {
let entry = createImportEntry(providerInfo);
importFile += entry;
resolve();
}))
);
}
, Promise.resolve() );
You can also use Promise.all but the out data may not be in the order that you expect if you append to the importFile variable.

I get Promise { <pending> } as returned value and also calling in async scope gives me undefined immediately

Im trying to return a value from a Promise in async-await form and use it in another function in another file, but I do have problem because my Promise doesnt return any value.
When im trying to console.log('website') it returns me undefined immediately (it's like the value is not being fetched at all from API services). I dont know what im doing wrong, I really love to learn about Promises and Async-Await but each time im trying to work with them im getting more confused.
const dns = require('dns')
const iplocation = require("iplocation").default;
const emojiFlags = require('emoji-flags');
const getServerIPAddress = async (server) => {
return new Promise((resolve, reject) => {
dns.lookup(server, (err, address) => {
if (err) throw reject(err);
resolve(address);
});
});
};
const getServerLocation = async (server) => {
const ip = await getServerIPAddress(server)
iplocation(ip).then((res) => {
const country = emojiFlags.countryCode(res.countryCode)
const result = `Location: ${country.emoji} ${country.name}`
return result
})
.catch(err => {
return `Location: Unknown`
});
}
(async function() {
console.log(await getServerLocation('www.google.com'))
})()
module.exports = {
getServerLocation
}
It is really important for me to get result from this function first, then use its value in another function. I wish you could give me tips on how to do tasks asynchronously.
You're clearly using async so it's not apparent why you're using then as well. If you use then then you must return the promise as well in order to preserve the promise chain:
const getServerLocation = async (server) => {
const ip = await getServerIPAddress(server)
return iplocation(ip).then((res) => {
const country = emojiFlags.countryCode(res.countryCode)
const result = `Location: ${country.emoji} ${country.name}`
return result
})
.catch(err => {
return `Location: Unknown`
});
}
Otherwise just async this:
const getServerLocation = async (server) => {
const ip = await getServerIPAddress(server)
let res = await iplocation(ip);
const country = emojiFlags.countryCode(res.countryCode)
const result = `Location: ${country.emoji} ${country.name}`
return result
}
const getServerLocation = async (server) => {
const ip = await getServerIPAddress(server)
//you need to return
return iplocation(ip).then((res) => {
const country = emojiFlags.countryCode(res.countryCode)
const result = `Location: ${country.emoji} ${country.name}`
return result
})
.catch(err => {
return `Location: Unknown`
});
}

JS return doesn't wait for await [duplicate]

This question already has answers here:
Using async/await with a forEach loop
(33 answers)
Closed 4 years ago.
I am trying to use the async await.
My app starts like this
axios.get(`${ROOT_URL}/ccidxann.php`)
.catch(err => console.error('axios error get', err))
.then(async res => {
const html = res.data
const jobList = await getJobList(html)
console.log(' after each jobList', jobList)
// jsonfile.writeFileSync('jobs.json', jobList, { flag: 'a' })
})
.catch(err => console.error(err))
The problem is that jobList is always returned as an empty array.
Here is the getJobList function
async function getJobList (html) {
const jobList = []
const dom = new JSDOM(html)
const { window } = dom
const $ = require('jquery')(window)
const jobsInDom = $('table').first().find('td > a')
// AWAIT HERE
await jobsInDom.each(async function (index, jobElem) {
const name = $(jobElem).text()
const url = $(jobElem).attr('href')
// Another AWAIT HERE
const jobDetailsHTML = await getJobDetailsHTML(`${ROOT_URL}/${url}`)
.catch(err => console.error('getJobDetailsHTML err', err))
const domDetails = new JSDOM(jobDetailsHTML)
const { window: windowDetails } = domDetails
const $details = require('jquery')(windowDetails)
const jobDetailsHTMLBody = $details('body').prop('outerHTML')
jobList.push({
_id: url,
name,
detailsHTML: jobDetailsHTMLBody
})
console.log('in each jobList', jobList)
}) //each
return jobList
}
As you can see I put the async keyword in front of it,
and I have put the async keyword in the callback of each
and put await in front of all async operations.
The console.log in the callback of each prints the array filled with values
but it seems the return works before the each
even with await keyword in front of it.
And also here is getJobDetailsHTML just in case. It's a simple function and seems to work just fine
async function getJobDetailsHTML (url) {
// ANOTHER AWAIT HERE
return await axios.get(url).data
}
I think jobsInDom.each is synchronous function, so putting await before doesn't give you desired effect. So somehow you need to get promise which resolved when processing is finished for all jobs, like this:
// handles one element and returns promise
const jobHandler = async jobElem => {
const name = $(jobElem).text()
const url = $(jobElem).attr('href')
const jobDetailsHTML = await getJobDetailsHTML(`${ROOT_URL}/${url}`)
.catch(err => console.error('getJobDetailsHTML err', err))
const domDetails = new JSDOM(jobDetailsHTML)
const { window: windowDetails } = domDetails
const $details = require('jquery')(windowDetails)
const jobDetailsHTMLBody = $details('body').prop('outerHTML')
jobList.push({
_id: url,
name,
detailsHTML: jobDetailsHTMLBody
})
console.log('in each jobList', jobList)
}
const promises = [];
// start processing of each element
jobsInDom.each((index, jobElem) => promises.push(jobHandler(jobElem));
// wait for processing of all job elements
await Promise.all(promises);

Categories