I need a solution to this below confusion i have .
I have a list of promises outer_promises_list.
Inside the promise.then of each promise in this outer_promises_list , I have another list of promises inner_promises_list
What I want is a method which gets called when all the outer promises and all the promises inside them get resolved .
Here is my code to get the context :
fetch = require("node-fetch")
function all_promises_resolved_method() {
console.log("All promises resolved ! ");
}
function start_main_process() {
let outer_promises_list = []
let inner_promises = []
start_urls_list.forEach(url => {
outer_promises_list.push(fetch(url).then(resp => resp.text()))
})
outer_promises_list.forEach((outer_prom) =>
{
outer_prom.then(htmlbody =>
{
inner_promises = inner_promises.concat(get_theater_promises());
inner_promises.forEach(inner_prom => {
inner_prom.then(inner_resp => {
inner_resp.clone().text().then(inner_html_body => {
console.log("do synchronous stuff on this html body ");
})
})
})
})
console.log("inner promises skipped , will be resolved later ");
Promise.all(inner_promises.concat(outer_promises_list)).then(all_promises_resolved_method)
})
}
function get_inner_promises() {
inner_promises = []
[url1, url2, url3].forEach((url)=>{
inner_promises.push(fetch(href))
})
return inner_promises;
}
start_main_process() ;
My issue is that all_promises_resolved_method is called only when the inner_promises of the first outer promise is resolved . I need a solution to call that method when all outer promises and each of all of those inner promises are resolved. What's the easiest way and efficient way to do this ?
It seems you missed a few things in your pseudocode or there is some typo. Also, you are calling Promise.all on outer_promises_list and inner_promises twice, not sure why. Following is my solution, anyway.
Please remember that JS Promise is designed to avoid nested code. So if you are using promises while nesting callbacks or Promises, most likely you are not doing it right.
fetch = require("node-fetch")
function all_promises_resolved_method() {
console.log("All promises resolved ! ");
}
function start_main_process() {
let outer_promises_list = []
let inner_promises = []
start_urls_list.forEach(url => {
outer_promises_list.push(fetch(url).then(resp => resp.text()))
})
Promise.all(outer_prom)
.then(htmlbodyArr => {//it is an array of resolved values from all the promises in outer_prom
/*
I don't know how you are using htmlBody to create inner promise.
Following is my best guess
*/
htmlbodyArr.forEach((htmlBody)=>{
inner_promises.push(get_theater_promises(htmlBody));
})
return inner_promises;
})
.then((inner_promises)=>{
return Promise.all(inner_promises)
})
.then((innerPromiseResultArr)=>{
all_promises_resolved_method();
})
.catch((err)=> console.log(err))
/*
outer_promises_list.forEach((outer_prom) =>
{
outer_prom.then(htmlbody =>
{
inner_promises = inner_promises.concat(get_theater_promises());
inner_promises.forEach(inner_prom => {
inner_prom.then(inner_resp => {
inner_resp.clone().text().then(inner_html_body => {
console.log("do synchronous stuff on this html body ");
})
})
})
})
console.log("inner promises skipped , will be resolved later ");
Promise.all(inner_promises.concat(outer_promises_list)).then(all_promises_resolved_method)
})
*/
}
function get_inner_promises() {
inner_promises = []
[url1, url2, url3].forEach((url)=>{
inner_promises.push(fetch(href))
})
return inner_promises;
}
start_main_process() ;
Related
I've searched a lot, tried many things but I can't come to a clean conclusion. I'm chaining a lot of Promises:
getInstalledComponents().call().then(response => {
return {
'installed_components': {
'data': response
}
};
}).then((passed_data) => {
return getDemosCategories().call().then(response => {
return {...passed_data, ...{'demos_categories': response}};
});
}).then((passed_data) => {
return getDemosData().call().then(response => {
return {...passed_data, ...{'demos_data': response}};
});
}).then((passed_data) => {
});
I can't handle errors with this. I'd like to re-write this in such a manner that if one of them fails, all of them should fail and return.
I tried to asy/await each promise function, reached nothing. The promises that return the data I need are getInstalledComponents, getDemosCategories, getDemosData and each of them is a Promise-based AJAX call. It's basically resolved when the AJAX call comes back.
This doesn't look clean, nor useful. How can I re-write this to fit my requirements?
Utilizing Promise.all we are able to parallelize requests:
Promise.all([
getInstalledComponents().call(),
getDemosCategories().call(),
getDemosData().call()
])
.then(([installedComponents, demosCategories, demosData]) => ({
"installed-categories": { data: installedComponents },
"demos-categories": { data: demosCategories },
"demos-data": {data: demosData}
}))
.catch(e => handleSomeErrorForAnyOfRequestsAbove(e))
Using async/await there still Promise.all will be needed:
const result = {};
try {
const [installedComponents, demosCategories, demosData] = await
Promise.all([
getInstalledComponents().call(),
getDemosCategories().call(),
getDemosData().call()
]);
result["installed-components"] = installedComponents;
result["demos-categories"] = demosCategories;
result["demos-data"] = demosData;
} catch(e) {
handleSomeErrorFromAnyOfRequestFailed();
}
If you were to simply put a catch block at the end of the last then it would catch errors in any of the functions.
Promise.reject("firstFailed")
.then(passedData => console.log("second") || passedData)
.then(passedData => console.log("third") || passedData)
.then(passedData => console.log("fourth") || passedData)
.catch(error => console.error(error));
As you can see from the lack of console logs in the example above, first rejection stops the execution of any other then block
I'm filling an array inside a loop and I need the full array when the loop finishes.
I've tried handling everything through promises or by using a counter but i can't seem to figure out the trick here.
lambda.listFunctions({}).promise()
.then((data) => {
data.Functions.forEach(func => {
lambda.listTags({ Resource: func.FunctionArn }).promise()
.then((data) => {
if ("Edge" in data.Tags) {
available_functions.push(func.FunctionName)
}
})
});
console.log(available_functions)
})
available_functions is always empty unless I console log it at the end of each foreach loop and then I have it returning 18 times which is not what I want.
I believe you can just promise it chain it to ensure all operations within the scope of the then completes before going down the chain.
lambda.listFunctions({}).promise()
.then(data => {
const { Functions } = data;
// I converted this from forEach to for of
for (const func of Functions) {
lambda.listTags({ Resource: func.FunctionArn }).promise()
.then(data => {
if ("Edge" in data.Tags) {
available_functions.push(func.FunctionName)
}
})
}
// also you can promise chain it if available_functions is within scope
})
.then(() => console.log(available_functions))
Or the cleaner async await way would look something like...
async fn() {
const available_functions = [];
const { Functions } = await lambda.listFunctions({}).promise();
for (const func of Functions) {
const tags = await lambda.listTags({ Resource: func.FunctionArn }).promise();
if ("Edge" in tags) {
available_functions.push(func.FunctionName)
}
}
return available_functions
}
Hope this helps!
You can use Promise.all with your problem. See documentation on Promise.all().
const available_functions = [];
lambda.listFunctions({}).promise()
.then((data) => {
const promises = []; // Collect promises
data.Functions.forEach(func => {
promises.push(lambda.listTags({ Resource: func.FunctionArn }).promise()
.then((data) => {
available_functions.push(func.FunctionName)
return Promise.resolve(available_functions);
})
);
});
Promise.all(promises)
.then(results => {
console.log(available_functions)
// or
console.log(results[results.length - 1]);
});
});
After a bunch of looking into Futures, Promises, wrapAsync, I still have no idea how to fix this issue
I have this method, which takes an array of images, sends it to Google Cloud Vision for logo detection, and then pushes all detected images with logos into an array, where I try to return in my method.
Meteor.methods({
getLogos(images){
var logosArray = [];
images.forEach((image, index) => {
client
.logoDetection(image)
.then(results => {
const logos = results[0].logoAnnotations;
if(logos != ''){
logos.forEach(logo => logosArray.push(logo.description));
}
})
});
return logosArray;
},
});
However, when the method is called from the client:
Meteor.call('getLogos', images, function(error, response) {
console.log(response);
});
the empty array is always returned, and understandably so as the method returned logosArray before Google finished processing all of them and returning the results.
How to handle such a case?
With Meteor methods you can actually simply "return a Promise", and the Server method will internally wait for the Promise to resolve before sending the result to the Client:
Meteor.methods({
getLogos(images) {
return client
.logoDetection(images[0]) // Example with only 1 external async call
.then(results => {
const logos = results[0].logoAnnotations;
if (logos != '') {
return logos.map(logo => logo.description);
}
}); // `then` returns a Promise that resolves with the return value
// of its success callback
}
});
You can also convert the Promise syntax to async / await. By doing so, you no longer explicitly return a Promise (but under the hood it is still the case), but "wait" for the Promise to resolve within your method code.
Meteor.methods({
async getLogos(images) {
const results = await client.logoDetection(images[0]);
const logos = results[0].logoAnnotations;
if (logos != '') {
return logos.map(logo => logo.description);
}
}
});
Once you understand this concept, then you can step into handling several async operations and return the result once all of them have completed. For that, you can simply use Promise.all:
Meteor.methods({
getLogos(images) {
var promises = [];
images.forEach(image => {
// Accumulate the Promises in an array.
promises.push(client.logoDetection(image).then(results => {
const logos = results[0].logoAnnotations;
return (logos != '') ? logos.map(logo => logo.description) : [];
}));
});
return Promise.all(promises)
// If you want to merge all the resulting arrays...
.then(resultPerImage => resultPerImage.reduce((accumulator, imageLogosDescriptions) => {
return accumulator.concat(imageLogosDescriptions);
}, [])); // Initial accumulator value.
}
});
or with async/await:
Meteor.methods({
async getLogos(images) {
var promises = [];
images.forEach(image => {
// DO NOT await here for each individual Promise, or you will chain
// your execution instead of executing them in parallel
promises.push(client.logoDetection(image).then(results => {
const logos = results[0].logoAnnotations;
return (logos != '') ? logos.map(logo => logo.description) : [];
}));
});
// Now we can await for the Promise.all.
const resultPerImage = await Promise.all(promises);
return resultPerImage.reduce((accumulator, imageLogosDescriptions) => {
return accumulator.concat(imageLogosDescriptions);
}, []);
}
});
Alright so I kinda got stuck doing nested queries with firestore because of ForEach and nested Promise returns. From all the previous posts on stackoverflow I understood quite a bit about promises and then nesting them inside ForEach. However, I couldnt find a solution where I nest promise inside forEach which then has another forEach inside it.
I do understand that I have to use Promise.all to make the root forEach wait but how to tell it to wait until the .then() also returns something?
firebase.firestore().collection("Feeds").get().then(feedsSnapshot => {
let promiseArray = [], anotherPromiseArray = [];
feedsSnapshot.forEach(function (feed) {
let promise = checkifILikedTheFeed(feed).then(like => {
return like;
}).then(like => {
let anotherPromise = firebase.firestore().collection("Feeds").doc(feed.id).collection("Likes").get()
.then(likesSnapshot => {
if(likesSnapshot.empty){
return {
// return myData
};
} else {
let countLikes = 0;
likesSnapshot.forEach(likeDoc => {
if(likeDoc.data().isLike){
countLikes++;
}
});
return {
//myData + countLikes
};
}
});
anotherPromiseArray.push(anotherPromise);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// I thought this would work, sadly doesnt.
});
promiseArray.push(promise);
});
return Promise.all([promiseArray,anotherPromiseArray]);
}).then(feeds => {
console.log(feeds);
// ^^^^^^^^^^^^^^^^^^^
// returns [[undefined],[undefined]]
dispatch({
type: 'RETRIEVE_FEEDS',
payload: feeds,
});
});
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)
})