最近在寫前臺,想起曾經被回調地獄支配的恐懼,希望能找到徹底摒棄回調函數的辦法。然後我去翻了一下ES6的新特性(emm...貌似已經不是新特性了?)遺憾的是,我並沒有找到現成的辦法。
不過,通過ES6所帶來的yield語法,這一點完全可以做到,下面
造了個輪子提供一個實現:
function syn(func) {
let method = {}
let gt = func.call(func, method);
method.exc = function() {
let target = arguments[0];
let args = [];
for(let i=1; i<arguments.length; i++) {
if(func==arguments[i]) {
args.push(function(){
gt.next(arguments);
});
} else {
args.push(arguments[i]);
}
}
target.apply(func, args);
};
gt.next();
}
十分簡短的代碼,但是足以實現我們的目的。事實上,出於便捷考慮,我們完全可以在函數內多定義一些常用的回調方法,比如require或onevent,當然也可以通過new的參數去動態增加,這些都十分容易實現,在此不多贅述。
下面我們先看一下如何來使用:
我們先定義一個callback函數,出於簡潔性考慮,這裏我用計時器的回調來模擬。然後new syn()實現函數的運行。
我們可以寫任意多個var val = yield method.exc();一定當上一個過程完成後纔會開始下一個過程,此時可以確保回調過程已經發生。在下面,我們用this來表達回調函數的書寫位置,用1,2,3來模擬回調函數的參數,在進行web請求的情況下,這應該是請求的結果。
該書寫形式只支持一個回調函數,顯然對於web請求來說只有success時才應該繼續下面的代碼,如果失敗,應當考慮用捕獲異常的方式去結束這段代碼。當然,如果你十分渴望對多回調的支持,那也並不難實現。
new syn(function*(method){
let param = yield method.exc(callback, "a", "b", this);
console.log(param[0]+","+param[1]+","+param[2]);
});
function callback(opt1, opt2, func) {
console.log(opt1+","+opt2);
setTimeout(func, 1000, "1", "2", "3");
}
注意到,我們唯一的縮進就是new syn,將函數定義爲function*,這樣對它的執行將返回一個迭代器,這個迭代器本質上是一組被yield語法切割的函數組合,也就是ES6提供的語法糖。每一個yield執行完成時都將返回一組新的參數,這組參數由gt.next提供。
回調函數也有一大堆參數,只不過這堆參數是以參數列表的形式寫上去的,既然這樣,我們完全可以接管回調函數,並且把回調函數的參數列轉移給gt.next,使gt.next擁有一個連貫的上下文。這也就是上述實現的核心思想。
顯而易見的,這個實現有一個缺點,那就是原本可以並行執行的過程將被迫變成順序執行,從而浪費時間,但從另一個角度來說,這也是順序執行代碼所必要的特點。
以上就是全部內容,感謝您的關注,我們下期有約【什麼,我沒上電視?】