Promise与遍历(循环,无穷多的then)遇到的问题及三个解决方案

今天碰到一个须要用Promise作无穷循环then的一个案例,顿时脑洞大开。
事情是这样的,有这样的一群异步函数,node

var func1 = function(callback){
    setTimeout(function(){
      console.log('foo');
      typeof(callback) !== 'function' || callback();
    }, 499);
};

var func2 = function(callback){
    setTimeout(function(){
      console.log('bar');
      typeof(callback) !== 'function' || callback();
    }, 500);
};

var func3 = function(callback){
    setTimeout(function(){
      console.log('foobar');
      typeof(callback) !== 'function' || callback();
    }, 501);
};
// ... more ...

将它们封装成Promise,依次放入一个数组内:数组

// promisify those callback functions
var promisify = function(func){
  return function(){
    return new Promise(function(resolve){
      func(resolve);
    });
  }
}

// array can be infinitely long
var func_arr = [promisify(func1), promisify(func2), promisify(func3)];

须要让数组里的每个Promise依次进行,最后一个执行完就结束。
个人第一个想法是这样的:闭包

// solution 1 failed
var master = [];

for (var i = 0; i < func_arr.length; i++) {
  master[i] = function(){
    if (i == 0) {
      return func_arr[i]();
    }
    else {
      return master[i-1]().then(function(){
        return func_arr[i]();
      })
    }
  };
};

master[master.length-1]();

乍看没有问题啊,每个新的master子函数的值是它上一个子函数的值加上一个then,可是一直报错——Maximum call stack size exceeded (node) / too much recursion (firefox)。
(12/24圣诞夜更新:已解决,修改了部分代码见下文)异步

僵了一会,仍是谷哥帮助了我,搜到一个很是简洁的方法:函数

// solution 2 // success
func_arr.reduce(function(cur, next) {
    return cur.then(next);
}, Promise.resolve()).then(function() {
    console.log('job finished');
});

看得我腿都拍出坑了,实际上是我想复杂了,没有必要在循环里不断地返回闭包函数等到最后调用,能够直接在循环里调用。
因而心有不甘,reduce()能实现的我大for岂有实现不了的道理,实则也不难,正确方法以下:oop

// solution 3 // success
var master = [];
master[0] = func_arr[0]();
for (var i = 1; i < func_arr.length; i++) {
  master[i] = master[i-1].then(func_arr[i]);
};

如下再提供一种重复调用函数式(就是递归)的方法:firefox

// solution 4 // success
function loop(i) {
  if (i != func_arr.length) {
    return func_arr[i]()
    .then(function() {
      return loop(i+1);
    });
  }
  return Promise.resolve(i);
}

loop(0).then(function(loop_times){
  console.log('job finished');
});

不知道你们还有没有更好的解决方案,或者能告知我第一个方案出错的缘由吧code

================递归

12/24更新

感谢wowhy的提示,将个人solution 1的代码加入一层闭包,已经解决了因内外函数变量产生的BUG:it

// solution 1 // success now
var master = [];

for (var i = 0; i < func_arr.length; i++) {
  master[i] = (function(j){
    return function(){
      if (j == 0) {
        return func_arr[j]();
      }
      else {
        return master[j-1]().then(function(){
          return func_arr[j]();
        })
      }
    }
  })(i);
};

var execute = master[master.length-1];
execute();

乍看这一串代码很长,对比以前的方案显得彻底多余,在我看来,能够生成在外部其余的上下文内进行独立调用的函数要更符合我大脑的思惟方式:)。

相关文章
相关标签/搜索