深刻淺出寫一個多級異步回調從基礎到Promise實現的Demo

今天一時興起,寫了一個漸進升級的異步調用demo,記錄一下。javascript

1. 最基礎的同步調用

//需求:f2在f1以後執行,且依賴f1的返回值。以下:
function f1(){
    var s="1";
    return s;
}
function f2(s){
    s+="-2";
    console.log(s);
}
f2(f1()); //"1-2"

2. 引入異步回調

//繼續,若是f1是個耗時操做,業務上須要作成異步,那麼就須要引入回調,以下:
function f1(){
    var s;
    setTimeout(function(){
        s="1";
        f2(s);
    },1000);
}
function f2(s){
    s+="-2";
    console.log(s);
}
f1(); //"1-2"

3. 回調函數名解耦

//對上面代碼,對f2作個函數名上的解耦,以下:
function f1(callback){
    var s;
    setTimeout(function(){
        s="1";
        callback(s);
    },1000);
}
function f2(s){
    s+="-2";
    console.log(s);
}
f1(f2); //"1-2"

//這樣,無論之後f2的function name如何變動,咱們都不須要去f1裏修改對他的引用了

4. 更多層級的異步回調

//那麼,咱們繼續,若是業務中引入了f3,且逐級依賴異步耗時操做f1和f2。以下:
function f1(){
    var s;
    setTimeout(function(){
        s="1";
        f2(s);
    },1000);
}
function f2(s){
    setTimeout(function(){
        s+="-2";
        f3(s);
    },1000);
}
function f3(s){
    s+="-3";
    console.log(s);
}
f1(); //"1-2-3"
//這時,該怎麼對f2和f3的function name解耦,以及怎樣保持一個相似f1().f2().f3()樣子的清晰的調用呢?

4.1 試着優雅一點

//思來想去,看起來須要引入更多的傳參,那搞兩個callback參數吧:
function f1(callback1,callback2){
    var s;
    setTimeout(function(){
        s="1";
        callback1(s,callback2);
    },1000);
}
function f2(s,callback){
    setTimeout(function(){
        s+="-2";
        callback(s);
    },1000);
}
function f3(s){
    s+="-3";
    console.log(s);
}
f1(f2,f3); //"1-2-3"
//WTF,尼瑪,這也太尼瑪髒了。函數名雖然解耦了,調用也很清晰。可是一個callback2參數須要在多個function之間傳遞,代碼可讀性變差;而且f1中傳入了並不須要處理的callback2,邏輯有些冗餘。

5. 觀察者模式拉平回調

從新思考下,看起來逐級依賴的函數回調,隨着層級的加深,在傳參和調用上都愈來愈吃力了。
咱們如今想辦法拉平一下這些回調,用自定義事件改造下。
專業術語上,叫觀察者模式,即經過自定義事件的監聽和觸發,來實現函數的依賴調用(f1觸發f2的調用)html

//註冊自定義事件 拆解f1 f2 f3的依賴回調關係致使的代碼邏輯上的嵌套(使用CustomEvent的detail屬性,實現參數傳遞)
document.addEventListener("f1:done",function(e){
    f2(e.detail);
});
document.addEventListener("f2:done",function(e){
    f3(e.detail);
});


function f1(){
    var s;
    setTimeout(function(){
        s="1";
        document.dispatchEvent(new CustomEvent('f1:done', {detail:s}));
    },1000);
    
}
function f2(s){
    setTimeout(function(){
        s+="-2";
        document.dispatchEvent(new CustomEvent('f2:done', {detail:s}));
    },1000);
}
function f3(s){
    s+="-3";
    console.log(s);
}

f1(); //"1-2-3"

注:阮一峯的這篇文章裏,還引入了一個訂閱/發佈模式,我的感受沒什麼意義,核心原理仍是事件註冊,參考:http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.htmljava

6. Promise的實現

上面的觀察者模式,看起來比較優雅了,可是由於拉平幾個有依賴關係的回調函數,就去註冊一些自定義事件,仍是感受有點怪。
而且,在調用上,只是寫了一個f1(),並不能在調用上看出三個函數的依賴關係。
ES6開始,引入了Promise概念,專門用來處理異步操做問題,參考:http://es6.ruanyifeng.com/#docs/promisees6

//繼續Promise方式,試着改寫一下:

function f1(){
    var p1=new Promise(function(resolve, reject) {
        var s;
        setTimeout(function(){
            s="1";
            resolve(s);
        },1000);
        
    });
    return p1;
}
function f2(s){
    var p2=new Promise(function(resolve, reject) {
        setTimeout(function(){
            s+="-2";
            resolve(s);
        },1000);
        
    });
    return p2;
}
function f3(s){
    s+="-3";
    console.log(s);
}

f1().then(function(s) {
    return f2(s);
}).then(function(s) {
    f3(s);
})

//看起來,還不錯哦
相關文章
相關標籤/搜索