【问题标题】:Calling the same function twice in a module fails在一个模块中调用相同的函数两次失败
【发布时间】:2017-12-19 18:52:32
【问题描述】:

我正在制作一个 node.js 模块来与机器人交互,我必须根据另一个 REST 调用的结果(两者都是 GET)获取 REST 资源。

当我这样做时,我首先在模块中调用一个函数来构造 REST 资源的 url,然后在“node-rest-client”模块中使用它。

问题是我需要将此模块用于多个机器人,这些机器人具有单独的 ip 地址和一些其他参数,这些参数因机器人而异(如果您愿意,我需要将其用作一个类)。

我的模块名为mir100.js,如下

var Client = require('node-rest-client').Client;
var rest = new Client(); 

/* Make the mir function act as a constructor
* use: var robot = new mir("192.168.42.111");
* or: var robot = new mir();//default ip = 192.168.12.20
*/
module.exports = mir;
function mir(ip) {
    this.g_robotPositions = [];
    this.HOST = "192.168.12.20";
    this.PORT = "8080";
    this.VERSION = "v1.0.0";
    if(arguments.length === 1){ 
        console.log("IP address is defined as argument: " + ip);
        this.HOST = ip; 
    }
}

/** Construct url
* Private function/method
* @param {string} path is the path of the url resource
* @param {boolean} predefined if true, this is indicates the path is taken from the MiR REST (includes /v1.0.0/...) default=false
* @return {string} url to the desired resource
*/
mir.prototype.ConstructURL = function(path, predefined){
    if(predefined == "undefined") predefined = false;
    if(predefined == true){
        return "http://" + this.HOST + ":" + this.PORT + path;
    }
    var url = "http://" + this.HOST + ":" + this.PORT + "/" + this.VERSION
    if(path[0] == '/') url += path;
    else url += "/" + path;
    return url;
}

/** Get all details about positions belonging to a specific map (guid) 
* @param map The map guid to get positions from
* @param callback A callback function, that returns any errors that might have occurred. The callback function takes the standard javascript argument format of (error, data)
*/
mir.prototype.GetPositionsFromMap = function(map,callback){
    g_robotPositions = [];
    var url = this.ConstructURL("maps/" + map + "/positions");

    var req = rest.get(url,function(data,response){
        if(response.statusCode >= 200 && response.statusCode < 300){

            var posCnt = data.length;
            for(var ii in data){
                console.log("data["+ii+"].url: " + data[ii].url);
                // This call to ConstructURL(...) fails and says "TypeError: this.ConstructURL is not a function"
                var tmpUrl = this.ConstructURL(data[ii].url);

                var req2 = rest.get(tmpUrl,function(data,response2){
                    if(response2.statusCode >= 200 && response2.statusCode < 300){
                        g_robotPositions.push(data);
                        if(g_robotPositions.length == posCnt){
                            callback(null,g_robotPositions);
                        }
                    }
                    else{
                        var error2 = {
                            "error":response2.statusCode,
                            "url":tmpUrl,
                            "robot":this.HOST,
                            "source":"mir100.js - GetPositionsFromMap()"
                        }
                        console.log(JSON.stringify(error2),error2.source,{"robot":this.HOST});
                        callback(error, null);
                    }
                });

                req2.on("error",function(err2){
                    console.log("Error GetPositionsFromMap 2: " + err2);
                });
            }
        }
        else{
            error = {
                "error":response.statusCode,
                "url":url,
                "robot":this.HOST,
                "source":"mir100.js - GetPositionsFromMap()"
            }
            console.log(JSON.stringify(error),error.source,{"robot":this.HOST});
            callback(error, null);
        }
    });

    req.on("error",function(err){
        console.log(err,"mir100.js - GetPositionsFromMap()",{"robot":this.HOST})
        callback(err, null);
    });
}

/** Get a list of all the maps on the robot 
* @param callback A callback function, that returns any errors that might have occurred. The callback function takes the standard javascript argument format of (error, data)
*/
mir.prototype.GetMaps = function(callback){
    var url = this.ConstructURL("maps");

    var req = rest.get(url,function(data,response){
        if(response.statusCode >= 200 && response.statusCode < 300){
            callback(null, data);
        }
        else{
            error = {
                "statusCode":response.statusCode,
                "url":url,
                "robot":this.HOST,
                "source":"mir100.js - GetMaps()"
            }
            console.log(JSON.stringify(error),error.source,{"robot":this.HOST});
            callback(JSON.stringify(error), null);
        }
    });

    req.on("error",function(err){
        console.log(err,"mir100.js - GetMaps()",{"robot":this.HOST})
        callback(err, null);
    });
}

在我的主 node.js 中,我有以下内容:

var mir = require('./mir100.js');

var robot = new mir();

robot.GetMaps( function(err,data){
    if(!err){
        robot.GetPositionsFromMap(data[1].guid,function(err,posData){
            if(!err){
                console.log("getPositionsFromMap: " + JSON.stringify(data));
            }
            else{
                console.log("Error getPositionsFromMap: " + err); 
            }
        });

    }
    else{
        console.log("Error GetMaps: " + err); 
    }
});

当我运行代码时,我在控制台中收到以下错误

TypeError: this.ConstructURL is not a function
    at C:\Projects\Active\AL10-2.0\al10-server\js\mir100.js:498:23
    at C:\Projects\Active\AL10-2.0\al10-server\node_modules\node-rest-client\lib\node-rest-client.js:539:13
    at Object.parse (C:\Projects\Active\AL10-2.0\al10-server\node_modules\node-rest-client\lib\nrc-parser-manager.js:142:13)
    at ConnectManager.handleResponse (C:\Projects\Active\AL10-2.0\al10-server\node_modules\node-rest-client\lib\node-rest-client.js:538:32)
    at ConnectManager.handleEnd (C:\Projects\Active\AL10-2.0\al10-server\node_modules\node-rest-client\lib\node-rest-client.js:531:18)
    at IncomingMessage.<anonymous    (C:\Projects\Active\AL10-2.0\al10-server\node_modules\node-rest-client\lib\node-rest-client.js:678:34)
    at emitNone (events.js:91:20)
    at IncomingMessage.emit (events.js:185:7)
    at endReadableNT (_stream_readable.js:974:12)
    at _combinedTickCallback (internal/process/next_tick.js:74:11)

为什么我不能使用这个功能两次? 我担心这与调用 ConstructURL() 的 for 循环有关,但我不明白为什么会出现问题......

【问题讨论】:

    标签: javascript node.js node-modules


    【解决方案1】:

    问题在于this 在函数内部调用时的上下文。

    rest.get声明回调函数时应使用bind(this)

    var req = rest.get(url,(function(data,response){...}).bind(this));
    

    而且,req.on("error", ...) 也是如此。

    这是GetPositionsFromMap的完整代码:

    /** Get all details about positions belonging to a specific map (guid) 
    * @param map The map guid to get positions from
    * @param callback A callback function, that returns any errors that might have occurred. The callback function takes the standard javascript argument format of (error, data)
    */
    mir.prototype.GetPositionsFromMap = function(map,callback){
        g_robotPositions = [];
        var url = this.ConstructURL("maps/" + map + "/positions");
    
        var req = rest.get(url,(function(data,response){
            if(response.statusCode >= 200 && response.statusCode < 300){
    
                var posCnt = data.length;
                for(var ii in data){
                    console.log("data["+ii+"].url: " + data[ii].url);
                    // This call to ConstructURL(...) fails and says "TypeError: this.ConstructURL is not a function"
                    var tmpUrl = this.ConstructURL(data[ii].url);
    
                    var req2 = rest.get(tmpUrl,function(data,response2){
                        if(response2.statusCode >= 200 && response2.statusCode < 300){
                            g_robotPositions.push(data);
                            if(g_robotPositions.length == posCnt){
                                callback(null,g_robotPositions);
                            }
                        }
                        else{
                            var error2 = {
                                "error":response2.statusCode,
                                "url":tmpUrl,
                                "robot":this.HOST,
                                "source":"mir100.js - GetPositionsFromMap()"
                            }
                            console.log(JSON.stringify(error2),error2.source,{"robot":this.HOST});
                            callback(error, null);
                        }
                    });
    
                    req2.on("error",function(err2){
                        console.log("Error GetPositionsFromMap 2: " + err2);
                    });
                }
            }
            else{
                error = {
                    "error":response.statusCode,
                    "url":url,
                    "robot":this.HOST,
                    "source":"mir100.js - GetPositionsFromMap()"
                }
                console.log(JSON.stringify(error),error.source,{"robot":this.HOST});
                callback(error, null);
            }
        }).bind(this));
    
        req.on("error",(function(err){
            console.log(err,"mir100.js - GetPositionsFromMap()",{"robot":this.HOST})
            callback(err, null);
        }).bind(this));
    }
    

    【讨论】:

    • 或者只使用继承父作用域上下文的箭头函数。
    • @PatrickRoberts,正确,但当前代码似乎根本没有使用 ES6。
    • 还可以在父作用域中存储引用,例如var that = this,并在回调中使用that 而不是this。顺便说一句,包装回调函数的括号不需要使用.bind(this),这些只是函数语句所必需的。那是一个函数表达式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-08
    • 1970-01-01
    相关资源
    最近更新 更多