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();

乍看這一串代碼很長,對比以前的方案顯得徹底多餘,在我看來,能夠生成在外部其餘的上下文內進行獨立調用的函數要更符合我大腦的思惟方式:)。

相關文章
相關標籤/搜索