如何使用太多嵌套函数优化此代码?

时间:2012-05-21 14:19:42

标签: javascript google-chrome asynchronous google-chrome-extension

我正在使用socket api编写Chrome扩展程序(虽然此文档已过时,最新版本的api为here),但我发现代码很难组织:

所有方法都在命名空间chrome.experimental.socket下,为简单起见,我只想使用下面的socket

socket.create("tcp", {}, function(socketInfo){
    var socketId = socketInfo.socketId;

    socket.connect(socketId, IP, PORT, function(result){
        if(!result) throw "Connect Error";        

        socket.write(socketId, data, function(writeInfo){
            if(writeInfo.bytesWritten < 0) throw "Send Data Error";

            socket.read(socketId, function(readInfo){
                if(readInfo.resultCode < 0) throw "Read Error";
                var data = readInfo.data; // play with the data
                // then send the next request
                socket.write(socketId, data, function(writeInfo){
                    socket.read(socketId, function(readInfo){
                        // ............
                    });
                });
            });
        })
    });
})

因为socket.writesocket.read都是异步的,我必须嵌套回调以确保在上一个请求得到正确响应后发送下一个请求。

管理这些嵌套函数真的很难,我怎么能改进它呢?

更新

我想要一个方法send,我可以用它作为:

send(socketId, data, function(response){
    // play with response
});
// block here until the previous send get the response
send(socketId, data, function(response){
    // play with response
});

2 个答案:

答案 0 :(得分:1)

这个怎么样?

var MySocket = {
  obj: null,
  data: null,
  start: function() { ... some code initializing obj data, ending with this.create() call },
  create: function() { ... some code initializing obj data, ending with this.connect() call },
  connect: function() { ... some connection code, ending with this.write() call },
  write: function() { ... some writing code that updates this.data, ending with this.read() call },
  read: function() { ... you probably get the idea at this point )) ... },
};

此对象可与MySocket.start()或其他内容一起使用。我们的想法是将所有数据(和嵌套调用)封装在单个(但更多的全局可用)对象中。

或者甚至更多,可以创建两个对象:一个纯粹用于写入目的,另一个用于纯粹阅读,每个对象都使用自己的data进行操作,然后将它们包装起来(以及它们的内部调用,可以这么说)到一个SocketManager对象。

答案 1 :(得分:0)

考虑使用异步延续传递样式,其中函数以SetInterval调用结束,并使用它们传递的函数。然后我们构造一个函数,它使用这种机制缠绕两个函数来相互调用。它的内容将是这样的:

var handle;
// pairs two functions
function pair(firstfunc, secondfunc, startarg) {
  var callbackToFirst = function(valuetofill) {
    handle = setInterval(firstfunc(valuetofill,callbackToSecond));
  };
  var callbackToSecond = function(valuetofill) {
    handle = setInterval(secondfunc(valuetofill,callbackToFirst));
  };

  callbackToFirst(startarg);
}

我们在这里做的是构造一对相互调用的回调,它们接受一个参数,每个参数都包含对两个内部调用函数的引用。然后我们通过调用第一个回调来启动该过程。

为一对示例读写函数构造该对(假设您已在封闭对象定义中设置了socketId):

// starts read/write pair, sets internal variable 'handle' to 
//   interval handle for control
function startReadWrite(initialarg, myDataFunc) {
    var readcall = function(value, func) {
        readSocket(getData(myDataFunc(func)));
    };
    var writecall = function(value, func) {
        writeSocket(checkBytesWritten(func));
    };
    handle = pair(readcall, writecall, initialarg);
}

对象的其余部分是这样的:

function myIO() {
    var socketInfo, socketId, handle;

    function create(func) {
        socket.create('tcp',{},function(thisSocketInfo) {
            socketInfo = thisSocketInfo;
        }
        setInterval(func(this),0);
    }

    function connect(IP, PORT, func) {
        socket.connect(p_socketId, IP, PORT, function() {
            if(!result) throw "Connect Error";
                setInterval(func(result),0);
            });
    }

    function readSocket(func) {
        socket.read(p_socketId, function(readInfo){ 
            setInterval(func(readInfo),0);
        });
    }

    function writeSocket(data, func) {
        socket.write(p_socketId, data, function(writeInfo){
            setInterval(func(writeInfo),0)
        });
    }

    function checkBytesWritten(writeInfo, func) {
        if(writeInfo.bytesWritten < 0) throw "Send Data Error";
        setInterval(func(writeInfo),0);
    }

    function getData(readInfo, func) {
        if(readInfo.resultCode < 0) throw "Read Error";
        var data = readInfo.data;
        setInterval(func(data),0);
    }


    //** pair and startReadWrite go here **//

}

最后调用整个过程:

var myIOobj = new myIO();
myIOobj.create(startReadWrite(myDataFunc));

注意:

  1. 这是为了展示一种风格,而不是现成的代码!不要只是复制并粘贴它。
  2. 不,我没有测试过这个;我做javascript但不是Chrome API的东西。我专注于回调机制等。
  3. 小心不同类别的回调;单个参数回调(如读取和写入回调),它采用单个值(可能由API定义)和2个参数回调(如大多数方法),它们接受参数和函数在结束时调用。
  4. getData方法接受回调并将data传递给它;这个回调(myDataFunc)是实际使用数据的函数。它需要将回调作为第二个参数并同步或异步调用它。
  5. TLDR:考虑使用异步调用来避免嵌套。我给出了一个机制的模糊示例,让两个函数连续使用这种样式相互调用,似乎是需要的。

    虽然我将其称为异步,但setInterval调用将以串行方式执行,但关键是在父调用完成后清除堆栈,而不是添加带嵌套的无限层。