Get data from first .then based on condition on second .then - javascript

I used promises as advised in my previous question to get values from 2 async calls.
But I want the results from my first call based on a condition of my second call. I keep getting undefined when I do what I am doing. How do I get my desired result.
First JSON:
let first_json = [
{
"company": "one"
},
{
"company": "two"
},
{
"company": "three"
}
]
The second JSON is dependent on the first one and is of similar format.
Using promises I did:
$.getJSON(first_json)
.then(first_data =>
first_data.map(d => {
return d.company;
})
)
.then(promises => Promise.all(promises))
.then(company => company.map(c => {
let second_json = json_string + c;
$.getJSON(second_json, function(data) {
if (data.length > 0) return c;
});
}))
.then(arr => {
console.log(arr);
});
arr for me is supposed to return ['one', 'three'] but is instead returning:
[undefined, undefined, undefined].
Why is that happening and how do I fix it?

Your callback is asynchronous, so, unless you 'await' it with a then, it won't be available to you right away, and therefore you can't act based on it.
Instead, do it like this:
$.getJSON(first_json)
.then(first_data =>
first_data.map(d => {
return d.company;
})
)
.then(promises => Promise.all(promises))
.then(company => company.map(c => {
let second_json = json_string + c;
return $.getJSON(second_json)
.then(data => {
if (data.length > 0) return c;
});
}))
.then(promises => Promise.all(promises))
.then(arr => {
console.log(arr);
});

You're applying the Promise.all in the wrong stage:
$.getJSON(first_json).then(first_data => {
const companies = first_data.map(d => {
return d.company;
});
const promises = companies.map(c => {
// ^^^^^^^^
let second_json = json_string + c;
return $.getJSON(second_json).then(data => {
// ^^^^^^
if (data.length > 0) return c;
});
});
return Promise.all(promises);
// ^^^^^^^^^^^
}).then(arr => {
console.log(arr);
});

Related

javascript - working with multiple promises inside loop - how to return data outside of promise?

I'm struggling to understand how I can return data from multiple promises to build up an array of data.
Is there anyway I can return the data outside of the promise to push to the data variable?
I have the following:
db_sh.find({
selector: {sh: req.params.sh_id},
fields: ['_id', 'sh_id', 'time'],
sort: ['_id']
}).then(function (result) {
let data = {};
console.log('Found: ' + result.docs.length);
if (result.docs.length > 0) {
for (var i = 0; i < result.docs.length; i++) {
let student = result.docs[i];
Promise
.all([getMealBooking(student._id), getStudentData(student._id)])
.then(function(response) {
var meal_booking_data = response[0];
var student_data = response[1];
console.log(meal_booking_data);
console.log(student_data);
})
.catch(function (err) {
return res.send(false);
});
data[student.time] = [
meal_booking_data,
student_data
]
}
}
/** Sort Data Oldest First*/
data = Object.keys(data).sort().reduce((a, c) => (a[c] = data[c], a), {});
console.log(data);
res.send(data);
});
I have two promises (getMealBooking() & getStudentData()): and I am using Promise.all() to return me the results of both of these promises. I have tried to return the data but I cannot get the results to build up the data array.
Any help to be able to build up a list of all my data would be great.
You need two Promise.alls - one to iterate over each student, and a nested one to fetch the getMealBooking and getStudentData for each student.
Put everything into an async function (that catches and sends false if needed) to make the control flow easier to understand.
const { docs } = await db_sh.find({
selector: { sh: req.params.sh_id },
fields: ['_id', 'sh_id', 'time'],
sort: ['_id']
});
if (docs.length === 0) {
// no data; stop here
res.send({});
return;
};
const data = {};
await Promise.all(
docs.map(student => (
Promise.all([getMealBooking(student._id), getStudentData(student._id)])
.then(([mealBookingData, studentData]) => {
data[student.time] = [mealBookingData, studentData];
})
))
);
const sortedData = Object.keys(data).sort().reduce((a, c) => (a[c] = data[c], a), {});
res.send(sortedData);
Another Promise.all() is needed for the loop that contains the Promise.all() you've already figured out. It's better to factor a little so you can see what's happening.
function getStudentMealAndData(student) {
return Promise
.all([getMealBooking(student._id), getStudentData(student._id)])
.then(function(response) {
var meal_booking_data = response[0];
var student_data = response[1];
console.log(meal_booking_data);
console.log(student_data);
return { student, meal_booking_data, student_data };
})
.catch(function (err) {
return res.send(false);
});
}
This simplifies the then block a bit...
}).then(function (result) {
console.log('Found: ' + result.docs.length);
let promises = []
for (var i = 0; i < result.docs.length; i++) {
let student = result.docs[i];
promises.push(getStudentMealAndData(student));
}
return Promise.all(promises);
}).then(results => {
// results are an array of [{ student, meal_booking_data, student_data }, ...]
let data = results.reduce((acc, s) => {
acc[s.student.time] = [ s.meal_booking_data, s.student_data ];
return acc;
}, {});
data = Object.keys(data).sort().reduce((a, c) => (a[c] = data[c], a), {});
console.log(data);
res.send(data);
});
let arr = [];
const datas = await Promise.all([
getMealBooking(),
getStudentData()
]);
arr.push(datas[0]); //adds getMealBooking() results
arr.push(datas[1]); // adds getStudentData() results

Returning Promised Array

I have the following object, stored in a variable ($gameSystem._ipLookupJSON):
{
"www.geoplugin.net/json.gp?jsoncallback=?": {
"IP": "geoplugin_request",
"Country": "geoplugin_countryCode",
"City": "geoplugin_city"
},
"gd.geobytes.com/GetCityDetails?callback=?": {
"IP": "geobytesipaddress",
"Country": "geobytescountry",
"City": "geobytescity"
},
"ip-api.com/json": {
"IP": "ip",
"Country": "country_name",
"City": "city"
},
"ipinfo.io/json": {
"IP": "ip",
"Country": "country",
"City": "city"
}
}
Each of the keys in this object is a URL.
I have a function ($._findService()) that:
Goes through each of these keys and sends them to another function
($._urlExists()), which checks if the URL is valid / responsive,
If true, $._findService() creates a new array with only the key and its elements,
And is supposed to return this new array.
Unfortunately, I am having problems with the third step- returning the new array.
I have Google'd and read as much as I can about Promises, .then, and Async/Await, but I just cannot figure it out and am at my wit's end just staring at these lines of code.
const isServiceAvailable = async url_to_check => {
console.log(url_to_check);
return await subaybay.an._urlExists("http://" + url_to_check);
};
const checkServices = async(json_data) => {
return await Promise.all(Object.keys(json_data).map(url_to_check => isServiceAvailable(url_to_check)));
};
$._findService = function(json_data) {
var url_check = checkServices(json_data);
url_check.then(function(values) {
for (i = 0; i < values.length; i++) {
if (values[i] === true) {
var service_to_use = new Promise(function(resolve, reject) {
var result = [];
result.push(json_data[Object.keys(json_data)[i]]);
result.unshift(Object.keys(json_data)[i]);
resolve(result);
});
service_to_use.then(function(value) {
console.log(value);
return value;
});
};
};
});
};
I am hoping for $._findService() to return an array.
But alas all I get is undefined.
I apologize if my code is not elegant or pretty- I have only been teaching myself JavaScript since the end of February.
Your problem was that you wasn't returning anything in the function scope and you should have return the promise(s).
const isServiceAvailable = url_to_check => subaybay.an._urlExists("http://" + url_to_check);
const checkServices = urls => Promise.all(urls.map(url_to_check => {
return {url: url_to_check,status: isServiceAvailable(url_to_check)}
}));
$._findService = async function(json_data) {
const values = await checkServices(Object.keys(json_data));
return values.filter(v => v.status).map(v => v.url);
};
You can then use:
const result = await $._findService(json_data)
or
$._findService(json_data).then(result => { /* Do something */ })
Note: when you return something from an async function you will get a promise, so, when you use await you are awaiting for the promise result inline.
There isn't and never will be any disadvantage to use async and await over a Promise, and, it's modern and better since you don't make more nested functions using "then" or "new Promise" syntax.
I would recommend a slight change to checkServices. Currently, the input type is an object but the output is promise of array. I think it would be more intuitive to return a promise of object, matching the original input -
// old function
checkServices({ "/foo": ..., "bar": ... })
// => Promise [ true, false ]
// new function
checkServices({ "/foo": ..., "bar": ... })
// => Promise { "/foo": true, "/bar": false }
Here's the changes -
// old function
const checkServices = async(json_data) => {
return await Promise.all(Object.keys(json_data).map(url_to_check => isServiceAvailable(url_to_check)));
};
// new function
const checkServices = (o = {}) =>
Promise.all(
Object.keys(o).map(k =>
isServiceAvailable(k).then(v => [ k, v ])
)
)
.then(Object.fromEntries)
With this result, it's easy to find all true keys for an object -
$._findService = (o = {}) =>
checkServices(o).then(o =>
Object.keys(o).filter(k => o[k])
)
$._findService({ "/foo": ..., "bar": ... })
// Promise [ "/foo" ]
Expand the snippet below to run this program in your browser -
const checkServices = (o = {}) =>
Promise.all(
Object.keys(o).map(k =>
isServiceAvailable(k).then(v => [ k, v ])
)
)
.then(Object.fromEntries)
// fake implementation for demo
const isServiceAvailable = (service = "") =>
new Promise (r =>
setTimeout (r, 1000, service === "/foo")
)
_findService = (o = {}) =>
checkServices(o).then(o =>
Object.keys(o).filter(k => o[k])
)
checkServices({ "/foo": 1, "/bar": 2 }).then(console.log, console.error)
// { "/foo": true, "/bar": false }
_findService({ "/foo": 1, "/bar": 2 }).then(console.log, console.error)
// [ "/foo" ]
There is no advantage to using async-await in this program

Asynchronous code in nested forEach loop- react native

I have input data that is formatted as such:
[ [4, 1, 2], [2, 5] ]
I want to make an api call for each of the numbers in the array, and have output as such:
[ [response_4, response_1, response_2], [response_2, response_5] ]
I've been stuck on this logic for two days-- I can't get my return array formatted correctly. It instead returns:
[ response_4, response_1, response _2, response_2, response_5 ]
I know I'm doing something wrong in terms of using promises/async, and also I know I need to reset temp to length = 0 at some point, but every time I add that in, it will simply return [] as my output. Any advice/help?
const getNumData = (data) => {
let temp = []
return new Promise((resolve, reject) => {
data.forEach((outerArray) => {
return new Promise((resolve, reject) => {
outerArray.forEach((number) => {
return fetch(`http://127.0.0.1:8000/api/number?id=${number}`, {method: 'GET',})
.then((response) => response.json())
.then((responseJson) => {
temp = this.state.seqDone.concat(responseJson[0]);
this.setState({
seqDone: temp
})
console.log(temp)
})
})
if (this.state.seqDone) {
console.log(this.state.seqDone)
resolve(this.state.seqDone);
} else {
reject(Error('Sequences not found'));
}
})
});
if (this.state.seqDone) {
console.log(this.state.seqDone)
resolve(this.state.seqDone);
} else {
reject(Error('Sequences not found'));
}
})
}
You can do it in this way
const nestedPromise = async (items = []) => {
return await Promise.all(
items.map(async item => {
if (Array.isArray(item) && item.length) {
return await nestedPromise(item)
}
// return await call to your function
return 'response-' + item
})
)
}
const items = [ [4, 1, 2], [2, 5] ]
nestedPromise(items).then(results => {
console.log(results)
})
Promise.all accepts array of functions as arguments, thoses functions will be executed asynchronously. In your case you just have to use it recursively
fetchData = (item) => {
return fetch(`http://127.0.0.1:8000/api/pose?id=${item}`)
.then (response => response.json())
}
constructArray = (items) => {
Promise.all(items.map(nestedArray => {
return Promise.all(nestedArray.map(this.fetchData))
}
))
.then((results) => {
console.log(JSON.stringify(results))
})
}

How to iterate with two arrays inside an array using the filter() method

How can i get it to enter .map() block?
Is it possible to do this or do i need to approach this issue in another way?
var orderCompetences = [];
var nActiveApplicants = [];
function MatchCompetences() {
var _applicantCompetenceResults = nActiveApplicants.filter(xApplicant => {
xApplicant.applicantCompetences.map(applComp =>
orderCompetence.map(orderComp => {
console.log(applComp ); //never gets called
console.log(orderComp);//never gets called
return applComp === orderComp;
}));
});
return Promise.all(_applicantCompetenceResults)
.then(resp => {
console.log(resp); // Never gets called
return 1;
})
.catch(err => {
console.log(err);
return 0;
});
}
An object inside nActiveApplicants
nApplicant = {
applicantID: "",
applicantPeriods: [],
applicantCompetences: [],
applicantFullAddress: "",
applicantDuration: ""
};
I presumed that you want some of the applicantCompetences to exist in orderCompetences, using the find method to search for a valid result, and then filtered it. I mapped the remaining records into Promises.
function MatchCompetences() {
var nActiveApplicants = [{
applicantID: "abcd",
applicantPeriods: [],
applicantCompetences: ['can read', 'can talk', 'useless mostly'],
applicantFullAddress: "",
applicantDuration: ""
},
{
applicantID: "efgh",
applicantPeriods: [],
applicantCompetences: ['can read', 'can talk', 'singer mostly', 'it-will-do'],
applicantFullAddress: "",
applicantDuration: ""
}
];
var orderCompetence = ['it-will-do'];
var _applicantCompetenceResults = nActiveApplicants.filter(xApplicant => {
return typeof xApplicant.applicantCompetences.find(elem => {
return orderCompetence.indexOf(elem) !== -1;
}) !== 'undefined'
}).map(item => {
return new Promise(resolve => {
resolve(item);
})
});
return Promise.all(_applicantCompetenceResults)
.then(resp => {
console.log('remaining applicants', resp); // Never gets called
return 1;
})
.catch(err => {
console.log(err);
return 0;
});
}
MatchCompetences()
I suspect you want something like this, although it's not clear to me why you'd wrap this in a Promise.all, since I'm pretty certain _applicantCompetenceResults won't be an array of Promises:
var orderCompetences = [];
var nActiveApplicants = [];
function MatchCompetences() {
var _applicantCompetenceResults = nActiveApplicants.filter(xApplicant =>
xApplicant.applicantCompetences.some(applComp => orderCompetences.includes(applComp))
);
return Promise.all(_applicantCompetenceResults)
.then(resp => {
console.log(resp);
return 1;
})
.catch(err => {
console.log(err);
return 0;
});
}
If this doesn't do the trick for you, you may want to try to explain what you're hoping to achieve, rather than get into the details of the code you're working with.

How return a value after a forEach loop using Promises?

I need to know how to return a value after a forEach loop using Promises. In this moment, when I launch my main, I get :
[ Promise { <pending> }, Promise { <pending> } ]
(my sampleidlist contains only 2 records)
This is my code :
MongoClient.connect("mongodb://127.0.0.1/myproject", function(err, db) {
return db.collection('RUN').find({
"idRun": query.idRun
}).toArray()
.then((out) => {
var sampleidlist = out[0].SAMPLE_ID
var pazlist = []
// Promisearr is the array of promises where I try to push the promises
var Promisearr = []
// there is the function find_paz that return idPaz for every sampleId in sampleidlist
function find_paz(sampleid) {
// I return a new Promise for every sampleId
// I want to create an array of idPaz
return new Promise((resolve, reject) => {
db.collection('PATIENTS').find({
"SAMPLE_ID": sampleid
}).toArray()
.then((pazArr) => {
var singlepaz = []
singlepaz.push(pazArr[0].idPaz)
return singlepaz
})
.then((singlepaz) => {
pazlist.push(singlepaz)
})
})
}
// Here the forEach loop
sampleidlist.forEach(sampleid => {
Promisearr.push(
find_paz(sampleid)
)
})
Promise.resolve(Promisearr)
.then(Promise.all(Promisearr))
.then(value => {
// value return {promise<pending>}
// I want that value is the array of idPaz
console.log(value)
}).catch((err) => {
console.log('errored', err);
})
}).catch((err) => {
console.log('errored', err);
})
})
Any suggest?
Thank you very much :)
You have it mixed up between Promise.all and Promise.resolve. Here:
return db.collection('RUN').find({
"idRun": query.idRun
}).toArray()
.then((out) => {
var sampleidlist = out[0].SAMPLE_ID
var pazlist = []
var Promisearr = []
function find_paz(sampleid) {
return db.collection('PATIENTS').find({
"SAMPLE_ID": sampleid
}).toArray()
.then((pazArr) => {
var singlepaz = []
singlepaz.push(pazArr[0].idPaz)
return singlepaz
})
.then((singlepaz) => {
pazlist.push(singlepaz)
return;
})
})
}
Promise.all(sampleidlist.map(find_paz))
.then(values => {
//values is an array with all the promises resolved
//pazlist should have your data.
}).catch((err) => {
console.log('errored', err);
})
}).catch((err) => {
console.log('errored', err);
})
Give it a try, let me know if you need clarification or if it doesn't work.
You are using Promise.resolve() and Promise.all() the wrong way. You should just call Promise.all() then .then(), like this :
Promise.all(Promisearr).then(value =>
console.log(value)
)

Categories

Resources