手记

如何改进你的 js 代码?关于JS实行回调的四大方式!

jser都知道,由于异步特性,在nodejs里,全是callback!!!一个不小心,下面的代码就会冒出来啦~~~

obj.func0( data0, function func1(data1){
    obj.func2( data1, function func3(data3){
        obj.func4( data3, function func5(data5){
            obj.func6( data5, function func7(data7){
                obj.func8( data7, function func9(data9){
                    ...
                });
            });
        });
    });
});

如果作为维护人员的你,看到这种代码,请放声哭泣吧~~~因为你遇到了 callback hell 啦!遇见地狱都不哭,那什么时候哭?

因此,作为开发人员的你,是否有必要放维护人员一条生路?救人一命,胜造七级浮屠,相信留人一命也有同样的效果哦。下面,就让我们以复制一个文件为例,来研究一下如何放下屠刀吧 ^_^

除了第一个主文件(入口文件),以下文件的命名均为 duplicate.js,存放于src目录下

主文件 index.js :

var hello = require("./src/duplicate.js");    // hello 对象依赖于duplicate.js文件
hello.copy("./src/duplicate.js","./duplicate_c.js");    //该函数对应duplicate.js里面的exports.copy

这是原生的代码模样 duplicate.js:

'use strict';                   //以下代码需要使用严格模式
var fs = require("fs");         //引入文件模块(nodejs自带模块)
// ( args ) => {} 是ES6的函数的一种模式(俗称箭头函数),作用是静态绑定函数内部的this,使this的指向不再发生变化
exports.copy = (source,target) => {    //source代表要复制的文件的路径,target则代表复制到的路径
    fs.open(source,"r",(err,fdr) => {   //以只读的方式打开源文件并获取文件句柄 fdr ,当然,原生回调中都以 错误返回变量 作为首参
        if( err ){  //如果返回了错误信息
            console.log( "打开源文件失败:" + err );
            return;
        }
        var buffer = new Buffer(2048);  //在内存中开辟一块2048字节大小的缓存区
        fs.read(fdr,buffer,0,buffer.length,0,(err,rSize,buffer) => {    //通过文件句柄 fdr 把文件流读到缓存中
            if( err ){
                console.log( "读取源文件失败:" + err );
                return;
            }
            //console.log( buffer.slice(0,rSize).toString() );
            fs.open(target,"w+",(err,fdw) => {      //当读取源文件成功后,再以写(如果不存在就创建新的文件)的方式打开目标文件,并获取其句柄 fdw
                if( err ){
                    console.log( "打开目标文件失败:" + err );
                    return;
                }
                fs.write(fdw,buffer,0,rSize,( err, wSize, buffer )  => {    //把缓冲区的文件流以固定格式写入到 fdw 所指向的目标文件中
                    if( err ){
                        console.log( "写入目标文件失败:" + err );
                        return;
                    }
                    console.log("写入文件成功,共写入" + wSize + "个字节");
                })
                fs.close(fdw,(err) => {     //通过句柄关闭文件
                    if( err ){
                        console.log( "关闭目标文件失败:" + err );
                    }
                });
            });
            fs.close(fdr,(err) => {     //通过句柄关闭文件
                if( err ){
                    console.log( "关闭源文件失败:" + err );
                }
            });
        });
    });
}

以上代码量还是挺少的。但是,算上 if 语句的层数,已经达到了七层之多,还是蛮吓人的哦。那么,我们如何减少代码层数,让伦家看起来不辣么累呢?继续上代码!

一、nodejs自带的 EventEmitter() 方法(生产者与消费者模式)duplicate.js:

'use strict';                   //以下代码需要使用严格模式
var fs = require("fs");         //引入文件模块(nodejs自带模块)
var events = require("events"); //引入事件模块(nodejs自带模块)
exports.copy3 = ( source, target ) => {
    var em = new events.EventEmitter();     // em 是由EventEmitter类 new 出来的一个对象,那么下面就可通过该对象声明以及跳转到不同的 cb回调函数中去

    em.on("opensource",() => {      // em.on("cbName") 可声明一个回调函数的位置,然后再通过 em.emit("cbName") 来执行该回调函数
        fs.open( source, "r", ( err, fd ) => {
            if( err ){
                console.log("打开源文件失败:",err.stack);
                return;
            }
            em.emit("readsource",fd);    //执行 “readsource” 回调函数,注意参数要对应好
        });
    }).on("readsource",( fd )=>{    //声明 “readsource” 回调函数
        var buffer = new Buffer(2048);
        fs.read( fd, buffer, 0, buffer.length, 0, ( err, readsize, buffer ) => {
            if( err ){
                console.log("读取源文件失败:",err.stack);
                return;
            }
            em.emit("opentarget", readsize, buffer);
        });
        fs.close( fd,( err )=>{
            if( err ){
                console.log("关闭源文件失败:");
            }
        });
    }).on("opentarget",( readsize, buffer )=>{
        fs.open( target, "w+", ( err, fd ) => {
            if( err ){
                console.log("打开目标文件失败:",err.stack);
                return;
            }
            em.emit("writesource", fd, readsize, buffer );
        });
    }).on("writesource",( fd, readsize, buffer )=>{
        fs.write( fd, buffer, 0, readsize, ( err, writesize, buffer ) => {
            if( err ){
                console.log("读取目标文件失败:",err.stack);
                return;
            }
            console.log("一共写入了",writesize,"个字节");
        });
        fs.close( fd,( err )=>{
            if( err ){
                console.log("关闭目标文件失败:");
            }
        });
    }).emit("opensource");      //在最后这里 emit("opensource") 以使该系列回调函数能够被调用到,这里相当于入口
}

可以看出,该模式明显减少了代码的层数,使代码的结构变得清晰。但是,该段代码也是有问题的?问题在哪呢?由于时间关系,本文无法一次写完,敬请期待我的更新吧!!!

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