Promise.all strange resolution issue - javascript

Consider these two functions when i call
getStatusAll(data)-
data=[[],['1'],['2'],['3']];
async function getStatusAll(data) {
console.log("In getStatusAll");
try{
let statusPromiseArray = data.map(async(value) => {
result= await this.fetchStatusDBs(value);
return result;
});
statusResolvedArray= await Promise.all(statusPromiseArray)
return statusResolvedArray;
}catch(err){
throw(err);
}
}
async function fetchStatusDBs(data) {
console.log("In fetchStatusDBs");
try{
//fetch status in dvf_req_id for an dvf_req_id
if(data.length==0){
console.log("1");
dvfStatus = await Promise.resolve("Disabled");
console.log("2");
trainingStatus = await Promise.resolve("Disabled");
console.log("3");
inferenceStatus = await Promise.resolve("Disabled");
}
else {
console.log("4");
dvfStatus = await Promise.resolve("Enabled");
console.log("5");
trainingStatus = await Promise.resolve("Enabled");
console.log("6");
inferenceStatus = await Promise.resolve("Enabled");
}
return [dvfStatus,trainingStatus,inferenceStatus];
}catch(err){
throw(err);
}
}
I am trying to resolve multiple Promises within a Promise.all
but the results is unexpected.
Actual Output-
In getStatusAll
In fetchStatusDBs
1
In fetchStatusDBs
4
In fetchStatusDBs
4
In fetchStatusDBs
4
2
5
5
5
3
6
6
6
[["Enabled","Enabled","Disabled"],["Enabled","Enabled","Enabled"],["Enabled","Enabled","Enabled"],["Enabled","Enabled","Enabled"]]
Expected Output-
In getStatusAll
inside map
In fetchStatusDBs
1
2
3
inside map
In fetchStatusDBs
4
5
6
inside map
In fetchStatusDBs
4
5
6
inside map
In fetchStatusDBs
4
5
6
[["Disabled","Disabled","Disabled"],["Enabled","Enabled","Enabled"],["Enabled","Enabled","Enabled"],["Enabled","Enabled","Enabled"]]
But changing fetchStatusDBs like this returns output in the correct format.
async function fetchStatusDBs(data) {
console.log("In fetchStatusDBs");
try{
if(data.length==0){
dvfStatus = "Disabled";
trainingStatus = "Disabled";
inferenceStatus = "Disabled";
}
else {
dvfStatus = "Enabled";
trainingStatus = "Enabled";
inferenceStatus = "Enabled";
}
return [dvfStatus,trainingStatus,inferenceStatus];
}catch(err){
throw(err);
}
}
Can somebody help me out?

You have several misunderstandings about async-await
async function getStatusAll(data) {
console.log("In getStatusAll");
try{
let statusPromiseArray = data.map(async(value) => { // map is sync
result= await this.fetchStatusDBs(value); // global result
return result; // return-await anti-pattern
});
statusResolvedArray= await Promise.all(statusPromiseArray) // global
return statusResolvedArray; // return-await anti-pattern
}catch(err){ // catch-throw anti-pattern
throw(err);
}
}
All of that can be written as -
function getStatusAll(data) {
return Promise.all(data.map(v => this.fetchStatusDBs(v)))
}
And any error will automatically bubble up. No need to catch and re-throw. This will do all the fetches in parallel. You could do the requests in serial if you wanted. This is shown to demonstrate proper use of await in async -
async function getStatusAll(data) {
const result = []
for (const value of data)
result.push(await this.fetchStatusDBs(value))
return result
}

Your main problem is that the status variables in fetchStatusDBs are undeclared and therefore implicitly global. Since your code is making multiple concurrent calls, which are interleaved at the await suspension points, they affect each other - a classic race condition. To fix this, just declare the variables with let or var.
const data=[[],['1'],['2'],['3']];
async function getStatusAll(data) {
console.log("In getStatusAll");
let statusPromiseArray = data.map(this.fetchStatusDBs, this);
return Promise.all(statusPromiseArray);
}
async function fetchStatusDBs(data) {
console.log("In fetchStatusDBs");
let dvfStatus, trainingStatus, inferenceStatus;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//fetch status in dvf_req_id for an dvf_req_id
if (data.length==0) {
console.log("1");
dvfStatus = await Promise.resolve("Disabled");
console.log("2");
trainingStatus = await Promise.resolve("Disabled");
console.log("3");
inferenceStatus = await Promise.resolve("Disabled");
} else {
console.log("4");
dvfStatus = await Promise.resolve("Enabled");
console.log("5");
trainingStatus = await Promise.resolve("Enabled");
console.log("6");
inferenceStatus = await Promise.resolve("Enabled");
}
return [dvfStatus, trainingStatus, inferenceStatus];
}
This will have the desired results (return value). To also get the expected output from the logs, you cannot use map with an asynchronous function and then wait for all the promises with Promise.all, but you need to make the calls in sequence:
async function getStatusAll(data) {
console.log("In getStatusAll");
const statusArray = [];
for (const value of data) {
// ^^^^^^^^^^^^^^^^^^^^^^^^^
statusArray.push(await this.fetchStatusDBs(value));
// ^^^^^
}
return statusArray ;
}

Related

How to wait on the for each function in js

I am trying to make api call and store the results in array. function itself is an async. Here's a code.
async function setPoliciesData(policies) {
let tmpPolicies = {};
await policies.forEach((policy) => {
const tmp = { ...policy };
// Here I need help. This returns promise Instade remembers How to wait till promise finishes
tmp["Name"] = getPolicyNameFromLocalStrage(policy.id);
try {
if (policy?.audience?.id) {
tmp["members"] = getMembersFromAudienceId(policy.audience.id);
} else {
tmp["members"] = [];
}
} catch (e) {
console.log(e);
}
let id = policy.id;
console.log("Setting policy ID : " + policy.id);
tmpPolicies[policy.id] = tmp;
});
console.log("Done the processing");
return tmpPolicies;
}
I am getting Promise object in return. I would want members returnd array.
I tried to console log and I am seeing that issue seems to be because of method is not async. What is proper way to fix it.
I refactored some of your code, but if you should make the function inside of the forEach asynchronous. In this case, I changed it to map to be a bit easier to follow. The key at the end is to return Promise.all() which will wait for all of the inner promises to be resolved before returning:
async function setPoliciesData(policies) {
const tmpPolicies = policies.map(async (policy) => {
policy.Name = await getPolicyNameFromLocalStrage(policy.id);
try {
policy.members = policy.audience && policy.audience.id
? await getMembersFromAudienceId(policy.audience.id)
: [];
} catch (e) {
console.error('Error: ' + e);
}
return policy;
});
console.log("Done the processing");
return Promise.all(tmpPolicies);
}

Combine async function result in separate function in more efficient way

I have two async functions, that separately work on their own - they each return data if I call them singularly, but I can't get a result from the second when I try and combine them. Basically I'm pinging an API and if the first function returns a result, use that data. If it returns empty, run the second function and use that data. I'm getting the data through a node proxy (again these work fine separately).
Function 1 that checks for a live event:
async function getLive(){
const response = await fetch("video-live");
const getLiveData = await response.json();
return getLiveData;
}
Function 2 that should run if Function 1 returns empty:
async function getArchived() {
const response = await fetch("video-archived");
const getArchivedData = await response.json();
return getArchivedData;
}
The final function that applies the logic:
function showVideo(getLiveData, getArchivedData) {
if( (getLiveData) == "" ) {
console.log('live'+getLiveData);
} else {
console.log('no live'+getArchivedData);
}
}
Then I call them like this:
getLive().then(showVideo);
The above returns 'no live' but not any data from the getArchivedData. How do I combine these in an elegant, efficient way? Can I combine both getLive and getArchived into one function with .then()?
Per #MichaelM's code:
async function getLive(){
const response = await fetch("video-live");
const getLiveData = await response.json();
return getLiveData;
}
async function getArchived() {
const response = await fetch("video-archived");
const getArchivedData = await response.json();
return getArchivedData;
}
async function showVideo() {
const liveData = await getLive();
if(liveData == "") {
console.log('live' + getLiveData);
} else {
const archivedData = await getArchived();
console.log('no live' + archivedData);
}
}
"Uncaught (in promise) ReferenceError: getLiveData is not defined"
Try rewriting your showVideo() function to use getLive() and getArchived() directly, instead of passing the results into showVideo(). Like this:
async function showVideo() {
const liveData = await getLive();
if(liveData == "") {
console.log('live' + liveData);
} else {
const archivedData = await getArchived();
console.log('no live' + archivedData);
}
}
Then you just call showVideo() without the .then() statement.

How to use promises so that loop doesn't hang

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
}

Recursive async/await request doesn't return to calling function

I'm doing a recursive request with async/await whenever the received response has length === 0. The problem is that when some request returns the desired data, the resolve(data); part of the promise doesn't seem to work.
So, in my code, I have reached the point where I get to make multiple recursive calls and, finally, receive a response whose length is not 0.
Note: there are plenty of API keys published in Github if you want to test the code.
var apiKey = "yourApiKey";
var url = "https://api.nasa.gov/mars-photos/api/v1/rovers/curiosity/photos?sol=";
function requestData(url) {
return fetch(url).then(response => {
if(response.ok) {
return response.json().then(data => {
return Promise.resolve(data);
});
} else {
return Promise.reject(response.status);
}
});
}
function NasaRequest(sun, limit, frecuency) {
return new Promise(async (resolve, reject) => {
var data = await requestData(url + sun + "&api_key=" + apiKey);
if(data.photos.length === 0 && !limit) {
setTimeout(async () => {
console.log("Delay for next request (sun " + sun + "): ", frecuency);
return await NasaRequest(sun - 1, limit, frecuency);
}, frecuency);
} else {
console.log("Resolve data:", data); // Code acutally reaches this point
resolve(data); // But this doesn't seem to work
}
});
};
async function init() {
try {
const currentValue = await NasaRequest(2175, false, 2000);
console.log("currentValue:", currentValue); // I want to reach this point, but is like the promise never returns
}catch(err){
console.error(err);
}
}
init();
In that moment, I want to return the data in the response to the calling init() function, for what I use resolve(data);. But it doesn't seem to work.
What am I doing wrong?
The problem is on setTimeout. When you calling setTimeout it returns right away and implicitly return undefined. The subsequence return doesn't matter at that point. If all you want to do, is to pause, and then proceed try something like this
async function requestData(url) {
var response = await fetch(url);
if (response.ok) {
return response.json()
} else {
throw new Error(response.status);
}
}
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function nasaRequest(sun, limit, freq) {
var data = await requestData(url + sun + "&api_key=" + apiKey);
if (data.photos.length === 0 && !limit) {
await sleep(freq);
console.log("Delay for next request (sun " + sun + "): ", freq);
return await nasaRequest(sun - 1, limit, freq);
} else {
console.log("Resolve data:", data);
return data;
}
};
async function init() {
try {
const currentValue = await nasaRequest(2175, false, 2000);
console.log("currentValue:", currentValue);
} catch (err) {
console.error(err);
}
}
init();
I added a simple sleep function to handle the pause. I also modified requestData (removed then and Promise parts).
Note that using this recursive approach you may run into stack overflow. To avoid that problem you can simply use a loop and check against your limit variable.

Node.js async/await not working

I am Korean and I do not speak English well so I ask for your understanding.
I am creating code that takes a search word and page number and crawls it.
let promises = []; //promise save
let totalData = []; //final data
let makeRequest = function(url) {
return new Promise((resolve, reject) => {
request(url, function(err, response, body){
if(body == null) { return reject('fail'); }
const $ = cheerio.load(body);
let parsingData = $('.g .r a');
console.log(parsingData);
for(let i = 0; i < parsingData.length; i++){
totalData.push(parsingData[i]);
}
return resolve('success');
});
});
}
//promises push
for(let i = 0; i < pageCount; i++){
let url = `https://google.com/search?q=${ searchWord }&start=${i}0`
promises.push(makeRequest(url));
}
(async function(){
try{
await Promise.all(promises);
console.log(totalData);
return res.json(true);
}
catch(error){
console.log(error);
return res.json(false);
}
});
I have confirmed that all promises change from pending to resolved.
enter image description here
enter image description here
 
 
I also noticed that the data I imported into the crawl was normal.
enter image description here
But I do not know why async and await do not work. console.log (totalData); does not execute.
This portion:
(async function(){
try{
await Promise.all(promises);
console.log(totalData);
return res.json(true);
}
catch(error){
console.log(error);
return res.json(false);
}
});
Looks as if you're trying to create an IIFE, but in this case you have created an anonymous function but you don't invoke it. So none of the code inside that function will execute. You need to add an extra set of parentheses to the end of that to invoke the function:
(async function(){
try{
await Promise.all(promises);
console.log(totalData);
return res.json(true);
}
catch(error){
console.log(error);
return res.json(false);
}
})(); // <-- note the extra () here

Categories