介面卡模式:非同步轉化為同步。

funnyok發表於2021-09-09

日常開發中會經常不知不覺的使用到各種設計模式,這裡以nodejs中非同步呼叫轉化為同步呼叫為例,一起探索下介面卡模式。(本文的旨在為把複雜的東西簡單化,理清思路換位思考,相信看完本文你應該會掌握處理複雜問題的思路)

如果熟悉nodejs的同學應該會對node各種非同步程式設計庫有所涉獵。這裡我不講那些庫,只是講解一個簡單的函式實現,以此來切入介面卡模式。

此時,我想把nodejs的非同步函式,改為promise的方式呼叫,即aa('xx').then().then();這樣。此時就需要一個介面卡,來轉化下我們的非同步函式,如果你的抽象思維比較好,腦子裡有一整套模型,那輕易就能寫出一個轉化函式。但是,很多時候我們沒有那麼高的抽象能力,怎麼辦呢?從結果入手。下面是一個例項:

改造前fs.readFile('xxx', function(data){});
期望改造後的結果是aaa('xxx).then(function(data) {});

aaa怎麼來的呢?就是透過介面卡模式生成的函式:var aaa = cb2Promise(fs.readFile);

從結果入手,那就容易了。首先要建立一個cb2Promise函式,然後他需要一個函式作為引數,返回一個可以呼叫函式,次函式呼叫的返回的結果是promise物件。 一步一步來:

function cb2Promise(func) {
    return function () {
        return new Promise(function(resolve, reject){});
    }
}

大體的構架已經出來了。現在就是完善內容。
第二步,我們傳入了一個函式,這個函式就是我們實際呼叫的東西:

function cb2Promise(func) {
    return function () {
        return new Promise(function(resolve, reject){
                func();
        });
    }
}

現在問題來了func需要兩個引數,第一個是url第二個是回撥。但是,現在引數從哪裡來呢?從結果來 aaa('xxx).then(function(data) {}); 這裡我們不是傳入了一個'xxx'了麼。這個怎麼獲取呢?此時有兩種方式,第一種最簡單的,直接以形參的方式傳入:

function cb2Promise(func) {
    return function (path) {//path從呼叫時傳入
        return new Promise(function(resolve, reject){
                func(path, function(err, data) {
                    if (err) {
                        reject(err);
                    } else {
                        resolve(data);
                    }
                });
        });
    }
}

這樣是不是很簡單的解決了問題。第二種方式是採用arguments來獲取,這種方式對擴充套件性比較友好,(你如你想fs.open('xxx', 'w+', function() {}))這個難道還得再寫一個函式麼,這不科學。所以用arguments來獲取引數擴充套件性更好。具體怎麼做呢?這時,我們想到了call和applay函式,而arguments為類陣列物件,很明顯此時我們用arguments函式:

function cb2Promise(func) {
    return function () {//path從呼叫時傳入
        var args = arguments;
        return new Promise(function(resolve, reject){
                func.apply(args , function(err, data) {//有問題
                    if (err) {
                        reject(err);
                    } else {
                        resolve(data);
                    }
                });
        });
    }
}

額,是不是感覺那裡不對了?是的 func.apply(args , function(err, data) {})此時引數有兩個,我們實際需要的只是一個陣列。所以,我們想著把回撥函式放到args中去。此時應該這樣做:

function cb2Promise(func) {
    return function () {
        var arg = arguments ? [].slice.apply(arguments) : [];
        return new Promise(function (resolve, reject) {
            arg.push(function(err, data) {
                if (err){
                    reject(err);
                } else {
                    resolve(data);
                }
            })
            func.apply(this, arg);
        })
    }
}

是不是很簡單,然後我們再稍微加一點錯誤處理,讓程式更健壯穩定:

function cb2Promise(func) {
    if (typeof func !== 'function') throw new Error('第一個引數必須為函式');
    return function () {
        var arg = arguments ? [].slice.apply(arguments) : [];
        return new Promise(function (resolve, reject) {
            arg.push(function(err, data) {
                if (err){
                    reject(err);
                } else {
                    resolve(data);
                }
            })
            func.apply(this, arg);
        })
    }
}

這樣一個簡單的介面卡就完成時,一步步下來是不是很簡單。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3137/viewspace-2798850/,如需轉載,請註明出處,否則將追究法律責任。

相關文章