手记

适配器模式:异步转化为同步。

日常开发中会经常不知不觉的使用到各种设计模式,这里以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);
        })
    }
}

这样一个简单的适配器就完成时,一步步下来是不是很简单。

0人推荐
随时随地看视频
慕课网APP