Use forEach () with promises, while accessing the previous promise leads to 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 am trying to apply these functions to every object in a JSON array:

 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 two things:

  • Is it good practice to apply forEach () on promises in the next question.

  • Are there any better ways to pass the previous promise results as a parameter in the then () chain? (I'm going through response param).

+7
javascript scope promise ecmascript-6 es6-promise
source share
4 answers

You can use .reduce() to access the 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 needed in ajaxRequest .

 const ajaxRequest = (url) => axios.get(url) .then((response) => { //console.log(response); return response; }) .catch((error) => { //console.log(error); }); 
+4
source share

The only problem with the code you provided is that the result with xmlParser is lost, the forEach loop just iterates, but does not save the results. To save the results, you will need to use Array.map, which will eventually receive Promise, and then Promise.all to wait and get all the results in the array.

I suggest using async / wait from ES2017, which simplifies working with promises. Since the provided code already uses arrow functions, which will require forwarding for compatibility of older browsers, you can add a translation plugin to support ES2017.

In this case, your code will look like this:

 function example() { return Promise.all([ array.map(async (value) => { try { const response = await ajaxRequest(value.url); return xmlParser(response); } catch(err) { console.error(err); } }) ]) } 

The above code will run all queries in parallel and return results when all queries end. You can also run and process requests one by one, this will also provide access to the previous promise, 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); } } } 
+1
source share

Another solution uses Promise.all for this, I think this is a better solution than the loop for 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 }); 
+1
source share

Are there any better ways to pass the previous results of promises as a parameter in the then () chain?

In fact, you can bind and enable promises in any order and anywhere in the code. One general rule — any chained promise with a then or catch branch — is just a new promise that needs to be bound later.

But there are no restrictions. Using loops, the most common solution is to reduce left side of foldl, but you can also use a simple let variable with reassignment with new promises.

For example, you can even design a delayed chain of promises:

 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) }) 
0
source share

All Articles