How can i get value from promise array? - javascript

I have a code which has an array of Promises
async function getResult(){
...
//resultPromise ==> it has promise in array Ex.[Promise { <pending> }, Promise { <pending> }]
let finalResult = await resultPromise.map((result) => {
//result has each promise from promise array
result.then((obj) => {
return obj.data
})
})
}
From this I want to get obj.data stored in the variable finalResult. How can I do that?
I tried below ways
return resultPromise.map((result)=>{
result.then((obj)=>{
console.log(obj.data)
return obj.data
})
})
or
resultPromise.map((result)=>{
result.then((obj)=>{
console.log(obj.data)
return obj.data
})
})
I know the chaining way but, I couldn't get the correct value from it.

Use Promise.all.
async function getResult(){
...
let finalResult = (await Promise.all(resultPromise)).map((obj) => obj.data);
...
}

Related

JavaScript, async/await and promises

I have a problem with async/await and some Promises.
I have this code. It starts here:
let valid = await LoadRouter.load(body);
console.log(valid);//showing me Pending Promises
The function is:
loadGeneratingUnits(data){
let newUGArray = [];
try {
const result = data.map(async (itemGU, index) => {
const checGU = await this.checkDataGu(itemGU.nombre);
if(!checGU){
let newUG = {
generating_unit_name: itemGU.nombre,
description: (!itemGU.descripcion) ? null : itemGU.descripcion,
it_generating_unit_id: (!itemGU.it_unidad_generadora) ? 0 : itemGU.it_unidad_generadora
}
newUGArray.push(newUG);
}
})
return result;
} catch (error) {
throw new Error(error.message)
}
}
This one is where I have the problems
async checkDataGu(guName = null){
if(guName){
return await generatingUnitModel.findOne({
attributes: [
'id',
'generating_unit_name',
],
where: {
generating_unit_name: guName
}
})
}
}
Any comment about the use of async/await on this code?
By making the callback to data.map() async, data.map() is now transforming the data into an array of Promises, because the return value of an async function is always a Promise. await only will wait for a Promise to resolve, not an array of them. You should use Promise.all for that:
const result = Promise.all(data.map(async (itemGU, index) => {
const checGU = await this.checkDataGu(itemGU.nombre);
if(!checGU){
let newUG = {
generating_unit_name: itemGU.nombre,
description: (!itemGU.descripcion) ? null : itemGU.descripcion,
it_generating_unit_id: (!itemGU.it_unidad_generadora) ? 0 : itemGU.it_unidad_generadora
}
newUGArray.push(newUG);
}
}))
Now result is one Promise that will resolve with an array of the values each inner Promise resolved with. Ultimately this means your upper let valid = await LoadRouter.load(body); should resolve with the array you expect.

Promise with Javascript

I'm struggling to understand Promise but it's really difficult for me.
it makes me crazy, I would appreciate your helps.
this is a code and I want "console.log(data)" after Recommend function finished
but the result is undefined.
What should I do?
many thanks
This is app.js
var _promise = function () {
return new Promise((resolve,reject) => {
var data = getJS.Recommend(req.query.User_id)
resolve(data)
}).then(function(data) {
console.log(data)
})
_promise();
}})
this is RecommendPost.js
exports.Recommend =(myId)=>{
var posts=[]
User.find({
User_id : myId
}).then((result)=>{
return User.find()
.select('User_id')
.where('age').equals(result[0].age)
}).then((User_id)=>{
return Promise.all(User_id.map((user,idx,arr)=>{
return Count.find()
.select('board_id')
.where('User_id').equals(user.User_id)
}))
}).then((Users_id)=>{
Users_id.forEach(items=>{
items.forEach(post=>{
posts.push(post.board_id)
})
})
}).then(()=>{
return getMax(posts);
})
}
cf. In RecommendPost.js, the posts works synchronously
//-------- I solved this problem! as some guys said, Recommend function should return Promise. So I edited, then this worked !
this is edited code. thank you for helping me :)
This is app.js
var _promise = function () {
return new Promise((resolve, reject) => {
getJS.Recommend(req.query.User_id).then((data) => {
resolve(data);
})
})
}
_promise().then((data) => { console.log(data) });
this is RecommendPost.js
exports.Recommend =(myId)=>{
return new Promise((resolve,reject)=>{
var posts=[]
User.find({
User_id : myId
}).then((result)=>{
return User.find()
.select('User_id')
.where('age').equals(result[0].age)
}).then((User_id)=>{
return Promise.all(User_id.map((user,idx,arr)=>{
return Count.find()
.select('board_id')
.where('User_id').equals(user.User_id)
}))
}).then((Users_id)=>{
Users_id.forEach(items=>{
items.forEach(post=>{
posts.push(post.board_id)
})
})
}).then(()=>{
resolve (getMax(posts));
})
})
}
It might be clearer for you with async/await that makes asynchronous code look like synchronous:
async function main() {
var data = await getJS.Recommend(req.query.User_id); // waits till its done before going to the next line
console.log(data);
}
// unimportant specific implementation details for live example:
const getJS = {
Recommend(id) {
data = "some data for user ";
return new Promise((res) => setTimeout(() => res(data + id), 1000));
}
}
const req = {
query: {
User_id: 5
}
}
main();
When you call to resolve function is mean that the function is finish and it returns to the caller.
the data object in the then doesn't exist
The Promise.resolve(value) method returns a Promise object that is
resolved with the given value. If the value is a promise, that promise
is returned; if the value is a thenable (i.e. has a "then" method),
the returned promise will "follow" that thenable, adopting its
eventual state; otherwise the returned promise will be fulfilled with
the value.
ES5 :
var _promise = function (req) {
return new Promise((resolve,reject) => {
getJS.Recommend(req.query.User_id).then((data)=>{
console.log(data)
resolve(data)
},(err)=>{
console.log(err)
reject(err);
})
})
}
_promise(req);
ES6
async function _promise(req) {
return await getJS.Recommend(req.query.User_id)
}
For your edit(The entire code):
In this case that Recommend function returns a promise, you don't need the _promise function that wraps it with another one.
and you call it direct
getJS.Recommend(req.query.User_id).then(
(data) => {
console.log(data)
});
Promises means it will always return something, either error or success.
if you resolve something then it will return from there. so your .then part will not execute.
so if you are using resolve and reject params in function then, use .catch method.
return new Promise((resolve,reject) => {
var data = getJS.Recommend(req.query.User_id)
resolve(data)
}) .catch(function (ex) { // in case of error
console.log(ex);
})

Conditional/dynamic array promise all

I have a function with an array of promises, that array can have from 1 to X promises.
Those promises enter into the array based on conditionals.
I want to be able to distinguish from which API comes each result, and I can't realise a clean way to do it
let promises = [];
if (false) {
let promise1 = request(toUrl);
promises.push(promise1);
}
if (true) {
let promise2 = request(toUrl);
promises.push(promise2);
}
if (false) {
let promise3 = request(toUrl);
promises.push(promise3);
}
if (true) {
let promise4 = request(toUrl);
promises.push(promise4);
}
try {
let result = await Promise.all(promises);
} catch (error) {
console.log(error);
}
So, if everything goes ok result will be an array of results. Not knowing which one of the conditionals was true, how do I know if result[0] is the result of promise1, promise2 or promise3?
You can just add to the response of your request(url) another information about the promise like
const promise1 = request(url).then(res => ({ res: res, promise: 'promise1' }))
and at the Promise.all() you will get values of the promises in the above form and can detect which promises were resolved.
Example
const promises = [];
if(true) {
const promise1 = fetch('https://jsonplaceholder.typicode.com/posts/1').then(res => ({ res: res, promise: 'promise1' }));
promises.push(promise1);
}
if(false) {
const promise2 = fetch('https://jsonplaceholder.typicode.com/posts/2').then(res => ({ res: res, promise: 'promise2' }));
promises.push(promise2);
}
Promise.all(promises).then(res => console.log(res));
In my opinion we can simplify the complexity of the problem by using following code -
let promises = [];
let truthyValue = true,
falsyvalue = true;
let [promise1, promise2, promise3, promise4] = await Promise.all([
truthyValue ? request(toUrl) : Promise.resolve({}),
truthyValue ? request(toUrl) : Promise.resolve({}),
falsyValue ? request(toUrl) : Promise.resolve({}),
falsyValue ? request(toUrl) : Promise.resolve({})
]);
// promise1 will be called only when truthyValue is set
if (promise1) {
// do something
}
// promise2 will be called only when truthyValue is set
if (promise2) {
// do something
}
// promise3 will be called only when falsyValue is set
if (promise3) {
// do something
}
// promise4 will be called only when falsyValue is set
if (promise4) {
// do something
}
I used an object map of promises with a name key in order to identify which resolve corresponds to which promise.
const promises = {};
const mapResolveToPromise = res => Object.fromEntries(
Object.entries(promises).map(([key], index) => [key, res[index]])
);
promises.promise1 = fetch('https://jsonplaceholder.typicode.com/posts/1');
promises.promise2 = fetch('https://jsonplaceholder.typicode.com/posts/2');
Promise.all(Object.values(promises))
.then(mapResolveToPromise)
.then(res => {
console.log(res.promise1.url);
console.log(res.promise2.url);
});
I had a similar problem, always a different quantity of async functions to call.
What I didn't want was to start the promises work before promise.all().
So I collected function pointers in an array.
eg:
async function first() {
return new Promise((resolve)=> setTimeout(resolve,1000,99));
}
async function second() {
return new Promise((resolve)=> setTimeout(resolve,1500,100));
}
let x = [first, second];
// x is transformed into an array with then executed functions
await Promise.all(x.map(x=>x()))
Result is:
[
99,
100
]
hopefully this helps and I understood the mentioned problem ... :)
Why all the pushes, you can construct arrays inline.
doPromiseStuff = async ({ thing = true }) => {
const urls = ['', '', '', ''];
return await Promise.all([
thing ? request(urls[1]) : request(urls[2]),
thing ? request(urls[3]) : request(urls[4])
]);
}

use forEach() with promises while access previous promise results in a .then() chain?

I have the following functions with promises:
const ajaxRequest = (url) => {
return new Promise(function(resolve, reject) {
axios.get(url)
.then((response) => {
//console.log(response);
resolve(response);
})
.catch((error) => {
//console.log(error);
reject();
});
});
}
const xmlParser = (xml) => {
let { data } = xml;
return new Promise(function(resolve, reject) {
let parser = new DOMParser();
let xmlDoc = parser.parseFromString(data,"text/xml");
if (xmlDoc.getElementsByTagName("AdTitle").length > 0) {
let string = xmlDoc.getElementsByTagName("AdTitle")[0].childNodes[0].nodeValue;
resolve(string);
} else {
reject();
}
});
}
I'm trying to apply those functions for each object in array of JSON:
const array = [{"id": 1, "url": "www.link1.com"}, {"id": 1, "url": "www.link2.com"}]
I came up with the following solution:
function example() {
_.forEach(array, function(value) {
ajaxRequest(value.url)
.then(response => {
xmlParser(response)
.catch(err => {
console.log(err);
});
});
}
}
I was wondering if this solution is acceptable regarding 2 things:
Is it a good practice to apply forEach() on promises in the following matter.
Are there any better ways to pass previous promise results as parameter in then() chain? (I'm passing response param).
You can use .reduce() to access previous Promise.
function example() {
return array.reduce((promise, value) =>
// `prev` is either initial `Promise` value or previous `Promise` value
promise.then(prev =>
ajaxRequest(value.url).then(response => xmlParser(response))
)
, Promise.resolve())
}
// though note, no value is passed to `reject()` at `Promise` constructor calls
example().catch(err => console.log(err));
Note, Promise constructor is not necessary at ajaxRequest function.
const ajaxRequest = (url) =>
axios.get(url)
.then((response) => {
//console.log(response);
return response;
})
.catch((error) => {
//console.log(error);
});
The only issue with the code you provided is that result from xmlParser is lost, forEach loop just iterates but does not store results. To keep results you will need to use Array.map which will get Promise as a result, and then Promise.all to wait and get all results into array.
I suggest to use async/await from ES2017 which simplifies dealing with promises. Since provided code already using arrow functions, which would require transpiling for older browsers compatibility, you can add transpiling plugin to support ES2017.
In this case your code would be like:
function example() {
return Promise.all([
array.map(async (value) => {
try {
const response = await ajaxRequest(value.url);
return xmlParser(response);
} catch(err) {
console.error(err);
}
})
])
}
Above code will run all requests in parallel and return results when all requests finish. You may also want to fire and process requests one by one, this will also provide access to previous promise result if that was your question:
async function example(processResult) {
for(value of array) {
let result;
try {
// here result has value from previous parsed ajaxRequest.
const response = await ajaxRequest(value.url);
result = await xmlParser(response);
await processResult(result);
} catch(err) {
console.error(err);
}
}
}
Another solution is using Promise.all for doing this, i think is a better solution than looping arround the ajax requests.
const array = [{"id": 1, "url": "www.link1.com"}, {"id": 1, "url": "www.link2.com"}]
function example() {
return Promise.all(array.map(x => ajaxRequest(x.url)))
.then(results => {
return Promise.all(results.map(data => xmlParser(data)));
});
}
example().then(parsed => {
console.log(parsed); // will be an array of xmlParsed elements
});
Are there any better ways to pass previous promise results as
parameter in then() chain?
In fact, you can chain and resolve promises in any order and any place of code. One general rule - any chained promise with then or catch branch is just new promise, which should be chained later.
But there are no limitations. With using loops, most common solution is reduce left-side foldl, but you also can use simple let-variable with reassign with new promise.
For example, you can even design delayed promises chain:
function delayedChain() {
let resolver = null
let flow = new Promise(resolve => (resolver = resolve));
for(let i=0; i<100500; i++) {
flow = flow.then(() => {
// some loop action
})
}
return () => {
resolver();
return flow;
}
}
(delayedChain())().then((result) => {
console.log(result)
})

Javascript - Promises stay as pending

Why do my promises stay in the pending state and how do I fix it?
var foundPeopleA = findPeopleA().then(function(result) {
var res = []
result.map(function(el) {
res.push(getProfileXML(el.sid));
});
return res
});
var foundPeopleB = findPeopleB().then(function(result) {
var res = []
result.map(function(el) {
res.push(getProfileXML(el.sid));
});
return res
})
return Promise.all([findPeopleA, findPeopleB]).then(function(results) {
console.log(results) //[ [ Promise { <pending> }, Promise { <pending> } ], [ Promise { <pending> }, Promise { <pending> } ] ]
})
However if i change the body of the 2 functions above into
var res
result.map(function(el) {
res = getProfileXML(el.sid);
});
return res
they won't be pending and I'll get the result.
Arrays are not promises. If you return an array of promises, then gets an array of promises - just like if you return any other non-promise value. Only if you return a promise, the promise will get executed before then. Your foundPeopleA and foundPeopleB each construct an array of promises; you need to concatenate those arrays and pass them to Promise.all or equivalent in order to get them executed.
The problem is that you are handling the promises fulfillment on each of them individually by using then, and all handles the fulfillment of multiple promises by passing it an array of unresolved promises. It builds a new promise with the results of all of them together. Just use:
Promise.all([findPeopleA(), findPeopleB()])
.then(function(responses) ...
Try assigning your Array to the result of the mapping.
var foundPeopleA = findPeopleA().then(function(result) {
var res = []
res = result.map(function(el) {
return getProfileXML(el.sid);
});
return res
});
Or, perhaps you can resolve the promise?
var foundPeopleA = findPeopleA().then(function(result) {
var res = []
res = result.map(function(el) {
return getProfileXML(el.sid);
});
resolve(res);
});
Either way, I belive you need to build your array by returning values from the mapping to create the new array.

Categories

Resources