Loop with native promises;

I'm trying to make an asynchronous loop with native ES6 promises. This works, but it’s wrong. I suppose I made a huge mistake somewhere, and I need someone to tell me where it is and how it is done correctly.

var i = 0; //creates sample resolver function payloadGenerator(){ return function(resolve) { setTimeout(function(){ i++; resolve(); }, 300) } } // creates resolver that fulfills the promise if condition is false, otherwise rejects the promise. // Used only for routing purpose function controller(condition){ return function(resolve, reject) { console.log('i =', i); condition ? reject('fin') : resolve(); } } // creates resolver that ties payload and controller together // When controller rejects its promise, main fulfills its thus exiting the loop function main(){ return function(resolve, reject) { return new Promise(payloadGenerator()) .then(function(){ return new Promise(controller(i>6)) }) .then(main(),function (err) { console.log(err); resolve(err) }) .catch(function (err) { console.log(err , 'caught'); resolve(err) }) } } new Promise(main()) .catch(function(err){ console.log('caught', err); }) .then(function(){ console.log('exit'); process.exit() }); 

Now the conclusion:

 /usr/local/bin/iojs test.js i = 1 i = 2 i = 3 i = 4 i = 5 i = 6 i = 7 fin error: [TypeError: undefined is not a function] error: [TypeError: undefined is not a function] error: [TypeError: undefined is not a function] error: [TypeError: undefined is not a function] error: [TypeError: undefined is not a function] error: [TypeError: undefined is not a function] error: [TypeError: undefined is not a function] caught [TypeError: undefined is not a function] exit Process finished with exit code 0 

The good part: it reaches the end.

The bad part: it picks up some bugs, and I don't know why.

+6
source share
5 answers

Any promise-driven helper function I've seen has actually made it much worse than what you can do out of the recursion box.

This is slightly better with .thenReturn , but yes:

 function readFile(index) { return new Promise(function(resolve) { setTimeout(function() { console.log("Read file number " + (index +1)); resolve(); }, 500); }); } // The loop initialization Promise.resolve(0).then(function loop(i) { // The loop check if (i < len) { // The post iteration increment return readFile(i).thenReturn(i + 1).then(loop); } }).then(function() { console.log("done"); }).catch(function(e) { console.log("error", e); }); 

See this at jsfiddle http://jsfiddle.net/fd1wc1ra/

This is pretty much equivalent to:

 try { for (var i = 0; i < len; ++i) { readFile(i); } console.log("done"); } catch (e) { console.log("error", e); } 

If you want to do nested loops, this is exactly the same:

http://jsfiddle.net/fd1wc1ra/1/

 function printItem(item) { return new Promise(function(resolve) { setTimeout(function() { console.log("Item " + item); resolve(); }, 500); }); } var mdArray = [[1,2], [3,4], [5,6]]; Promise.resolve(0).then(function loop(i) { if (i < mdArray.length) { var array = mdArray[i]; return Promise.resolve(0).then(function innerLoop(j) { if (j < array.length) { var item = array[j]; return printItem(item).thenReturn(j + 1).then(innerLoop); } }).thenReturn(i + 1).then(loop); } }).then(function() { console.log("done"); }).catch(function(e) { console.log("error", e); }); 
+7
source

If all you are trying to do is 7 using promises, then this will do it:

 function f(p, i) { return p.then(function() { return new Promise(function(r) { return setTimeout(r, 300); }); }) .then(function() { console.log(i); }); } var p = Promise.resolve(); for (var i = 0; i < 8; i++) { p = f(p, i); } p.then(function() { console.log('fin'); }) .catch(function(e) { console.log(e.message); }); 

The loop with promises is difficult because it is almost impossible not to fall into the JavaScript loop closure in the loop trap , but it is doable. The above works because it pushes all use of .then () into a subfunction f loop (i.e., Far from the loop).

The safer solution I'm using is to drop loops and look for patterns like forEach and reduce when possible, because they effectively force you to a subfunction:

 [0,1,2,3,4,5,6,7].reduce(f, Promise.resolve()) .then(function() { console.log('fin'); }) .catch(function(e) { console.log(e.message); }); 

here f is the same function as above. Give it a try .

Update: In ES6, you can also use for (let i = 0; i < 8; i++) to avoid the “loop in loop” trap without inserting code into the f subfunction.

PS: The error in your example .then(main(), - it should be .then(function() { return new Promise(main()); }, , but in fact, I think that you are using it incorrectly template. main() should return a promise, not wrap it.

+2
source

Try registering err.stack instead of err when looking for promises.

In this case, it looks like resolve and reject not defined in an anonymous function that receives a return from main after the completion of the initial iteration. I cannot fully follow your control flow, but that seems reasonable - after 7 iterations are completed, there should no longer be any new promises. However, it seems that the code is still trying to work, as it is for promises.

Edit: this is the problem .then(main(),function (err) { . A call to main alone will resolve and reject inside the anonymous function undefined. By the way I read it, main can only be called as an argument to the Promise constructor.

0
source

I also searched for various solutions and could not find the one who satisfied me, so I created my own. Here it is in case this is useful to someone else:

The idea is to create an array of promise generators and pass this array to a helper function that will perform promises one by one.

In my case, the helper function is simply this:

 function promiseChain(chain) { let output = new Promise((resolve, reject) => { resolve(); }); for (let i = 0; i < chain.length; i++) { let f = chain[i]; output = output.then(f); } return output; } 

Then, for example, to load multiple URLs one by one, the code would look like this:

 // First build the array of promise generators: let urls = [......]; let chain = []; for (let i = 0; i < urls.length; i++) { chain.push(() => { return fetch(urls[i]); }); } // Then execute the promises one after another: promiseChain(chain).then(() => { console.info('All done'); }); 

The advantage of this approach is that it creates code that is relatively close to a regular loop and with minimal indentation.

0
source

I had a similar need and tried to accept the answer, but I had a problem with the order of operations. Promise.all is a solution.

 function work(context) { return new Promise((resolve, reject) => { operation(context) .then(result => resolve(result) .catch(err => reject(err)); }); } Promise .all(arrayOfContext.map(context => work(context))) .then(results => console.log(results)) .catch(err => console.error(err)); 
0
source

Source: https://habr.com/ru/post/981507/


All Articles