与本地承诺循环;
我试图做一个asynchronous循环与本地ES6承诺 这种作品,但不正确的。 我想我在某个地方犯了一个大错,我需要有人告诉我它在哪里以及它是如何正确完成的
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() });
现在输出:
/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
好的部分:它到达最后。
不好的部分:它捕捉到一些错误,我不知道为什么。
任何带有promise循环的helper函数我已经看到,实际上它比使用recursion开箱即可做得更差。
这是一个更好的.thenReturn
但耶:
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); });
在jsfiddle中查看http://jsfiddle.net/fd1wc1ra/
这几乎完全等同于:
try { for (var i = 0; i < len; ++i) { readFile(i); } console.log("done"); } catch (e) { console.log("error", e); }
如果你想做嵌套循环,它是完全一样的:
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,那么这将做到这一点:
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); });
循环承诺是很难的,因为几乎不可能陷入循环陷阱中的 JavaScript 闭包 ,但这是可行的。 上面的工作是因为它将.then()全部用于循环的子函数f
(即远离循环)。
我使用的一个更安全的解决scheme是完全放弃循环,找出像forEach
这样的模式,并尽可能地reduce
它们,因为它们有效地强制了你的子function:
[0,1,2,3,4,5,6,7].reduce(f, Promise.resolve()) .then(function() { console.log('fin'); }) .catch(function(e) { console.log(e.message); });
这里f
是和上面相同的function。 试试吧 。
更新: 在ES6中,你也可以使用for (let i = 0; i < 8; i++)
来避免在代码中插入子函数f
中的“闭环”陷阱。
PS:你的例子中的错误是.then(main(),
它需要是.then(function() { return new Promise(main()); },
但是,我认为你使用的模式错误main()
应该返回一个promise,而不是被一个包装。
在捕获承诺错误时,尝试loggingerr.stack
而不是错误。
在这种情况下,在初始迭代完成之后,它看起来像在从main
返回的匿名函数中没有定义resolve
和reject
。 我不能完全按照你的控制stream程,但这似乎是有道理的 – 在7次迭代完成后,不应再有任何新的承诺。 但是,似乎代码仍然试图运行,就像有更多的承诺要解决。
编辑:这是问题。然后.then(main(),function (err) {
。调用自己的main
将导致resolve
和reject
匿名函数内部是未定义的。从我读的方式, main
只能被调用为Promise
构造函数的一个参数。
我有类似的需求,并尝试接受的答案,但我在操作的顺序有问题。 Promise.all
是解决scheme。
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));
我已经四处寻找各种解决scheme,找不到满意的,所以我最终创造了我自己的。 在这种情况下,对其他人有用:
这个想法是创build一个承诺发电机的数组,并给这个数组一个接一个执行承诺的帮助函数。
在我的情况下,辅助函数就是这样的:
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; }
然后,例如,要一个接一个地加载多个URL,代码将如下所示:
// 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'); });
这种方法的优点是它创build的代码相对于常规的for循环来说相当接近,而且缩进量最小。