鍍金池/ 問(wèn)答/HTML/ 使用reduce組合無(wú)限的函數(shù)調(diào)用鏈

使用reduce組合無(wú)限的函數(shù)調(diào)用鏈

一個(gè)對(duì)象, 對(duì)象中有before與enter方法, 其中 before 可以執(zhí)行異步任務(wù), 完成后執(zhí)行回調(diào)再進(jìn)入enter.
現(xiàn)在可能有若干個(gè)這樣的對(duì)象,要求按順序依次執(zhí)行下來(lái). 示例如下:

var a = {
    before: function(obj, next) {
        console.log("a before!");
        setTimeout(function() {
            next(obj);
        }, 1000);
        return false;
    },
    enter: function(obj) {
        console.log("a entered!");
    }
};
var b = {
    parent: a,
    before: function(obj, next) {
        console.log("b before!");
        setTimeout(function() {
            next(obj);
        }, 1000);
        return false;
    },
    enter: function(obj) {
        console.log("b entered!");
    }
};
var c = {
    parent: b,
    before: function(obj, next) {
        console.log("c before!");
        setTimeout(function() {
            next(obj);
        }, 1000);
        return false;
    },
    enter: function(obj) {
        console.log(" c entered!", obj);
    }
};

// 組裝后的函數(shù)
var fnChain = function(obj) {
    a.before(obj, function(obj) {
        a.enter(obj);
        b.before(obj, function(obj) {
            b.enter(obj);
            c.before(obj, function(obj) {
                c.enter(obj);
            });
        });
    });
};

fnChain({ Status: 1, Data: { name: "Anonymous" } });

示例中的 fnChain 是我手寫出來(lái)的, 現(xiàn)實(shí)中不能這樣. 我想實(shí)現(xiàn)一個(gè)函數(shù), 可以根據(jù)對(duì)象隊(duì)列, 自動(dòng)生成這個(gè) fnChain

比如我已經(jīng)有這么一個(gè)隊(duì)列, 然后傳給那個(gè)函數(shù):

var arr = [d, c, b, a];
getFnChain(arr); // 得到一個(gè) fnChain

怎么實(shí)現(xiàn)這個(gè) getFnChain 呢? 我有一個(gè)初步想法是依靠 reduce, 但不幸的是我試了半天, 頭都暈了, 也沒(méi)想出來(lái) ╮(╯Д╰)╭ . 看來(lái)對(duì) reduce的理解還差得遠(yuǎn).

希望有大神能幫助我. 可以參照示例代碼中的 fnChain , 這就是我最終想獲得的 函數(shù)

能順帶講一下原理就更好了 (?_?)

回答
編輯回答
尐潴豬

先看輸出:
"a before!"
"a entered!"
"b before!"
"b entered!"
"c before!"
"c entered!"
"d before!"
"d entered!"

function Node(name) {
  this.before = function(obj, next) {
    console.log(name + " before!")
    setTimeout(function() {
      next(obj)
    }, 1000)
    return false
  }
  this.enter = function(obj) {
    console.log(name + " entered!")
  }
}


function chain(curr, cb){
  return function(obj){
    curr.before(obj, function(obj){
      curr.enter()
      cb && cb()
    })
  }
}

let arr = ['a', 'b', 'c', 'd']
arr = arr.map(name => new Node(name))
let fnChain = arr.reduceRight((acc, curr) => chain(curr, acc)
                              , null) // initialValue is null
fnChain({ Status: 1, Data: { name: "Anonymous" } })

解釋下 reduce 的執(zhí)行過(guò)程(4次回調(diào))

callback acc curr return value
1 null d fnD = chain(d, null)
2 fnD c fnC = chain(c, fnD)
3 fnC b fnB = chain(b, fnC)
4 fnB a fnA = chain(a, fnB)
2018年8月2日 18:35