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


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

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的調用)

//注冊自定義事件 拆解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.html

6. Promise的實現

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

//繼續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);
})

//看起來,還不錯哦

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM