【问题标题】:"Call stack" for callbacks in node.jsnode.js 中回调的“调用堆栈”
【发布时间】:2014-06-08 04:45:48
【问题描述】:

我习惯于使用 Java 进行思考,并且正在尝试了解 node.js。当出现问题时,我的程序需要记录信息,我发现我必须在我的 node.js 程序中放入大量样板代码才能获得我在 Java 中免费获得的内容。

我的问题归结为:

  • 是否有一种更简单/非样板的方法可以在回调链中获取类似堆栈的信息?和/或
  • 我是否因为未能正确掌握 node.js 并试图强制异步 node.js 更像同步 Java 而感到内疚?

Java 示例

这是一个尝试(但失败)连接到 Mongo 数据库的 Java 程序: 导入 java.net.UnknownHostException;

import com.mongodb.Mongo;

public class Test {

    public static void main(final String[] args) throws UnknownHostException {
        final Mongo mongo = a();
    }

    private static Mongo a() throws UnknownHostException {
        return b();
    }

    private static Mongo b() throws UnknownHostException {
        return c();
    }

    private static Mongo c() throws UnknownHostException {
        return new Mongo("non-existent host");
    }

}

...这提供了有用的堆栈输出:

Exception in thread "main" java.net.UnknownHostException: non-existent host
at java.net.Inet6AddressImpl.lookupAllHostAddr(Native Method)
at java.net.InetAddress$1.lookupAllHostAddr(Unknown Source)
at java.net.InetAddress.getAddressesFromNameService(Unknown Source)
at java.net.InetAddress.getAllByName0(Unknown Source)
at java.net.InetAddress.getAllByName(Unknown Source)
at java.net.InetAddress.getAllByName(Unknown Source)
at java.net.InetAddress.getByName(Unknown Source)
at com.mongodb.ServerAddress.updateInetAddress(ServerAddress.java:204)
at com.mongodb.ServerAddress.<init>(ServerAddress.java:73)
at com.mongodb.ServerAddress.<init>(ServerAddress.java:46)
at com.mongodb.Mongo.<init>(Mongo.java:138)
at Test.c(Test.java:20)
at Test.b(Test.java:16)
at Test.a(Test.java:12)
at Test.main(Test.java:8)

(特别是,最后 4 行向我展示了 Mongo 错误发生时我自己的代码中的“发生了什么”。)

Node.js 示例

这是我在 node.js 中重新编写程序的尝试:

a(function (err, mongo) {
    if (err) {
        console.log("Something went wrong in main");
        console.log(err);
    }
});

function a(callback) {
    b(function (err, mongo) {
        if (err) {
            console.log("Something went wrong in a()");
            return callback(err);
        }

        return callback(null, mongo);
    });
}

function b(callback) {
    c(function (err, mongo) {
        if (err) {
            console.log("Something went wrong in b()");
            return callback(err);
        }

        return callback(null, mongo);
    });
}

function c(callback) {
    var MongoClient = require('mongodb').MongoClient;
    return MongoClient.connect('mongodb://non-existent host/', function (err, mongo) {
        if (err) {
            console.log("Something went wrong in c()");
            return callback(err);
        }

        return callback(null, mongo);
    });
}

...给出这个输出:

Something went wrong in c()
Something went wrong in b()
Something went wrong in a()
Something went wrong in main
[Error: failed to connect to [non-existent host:27017]]

但是为了得到这个输出,我必须在整个程序中放入大量样板代码,随着我的程序变得越来越大并且我有一个完整的开发团队,这对警察来说会很痛苦。

我可以通过另一种方式获得这种类似堆栈的输出吗?期待这种输出是非节点式的吗?

【问题讨论】:

  • Promise 可能是你想要的;它们以一致的方式传达(异步)流程的典型成功和失败。

标签: javascript node.js asynchronous stack-trace control-flow


【解决方案1】:

Promise 正是您正在寻找的(将堆栈功能带回异步代码)

var Promise = require("bluebird");
var mongodb = require("mongodb");
// enable long stack traces, bluebird specific
Promise.longStackTraces();
// promisify mongodb so that it returns promises, also bluebird specific
Promise.promisifyAll(mongodb);
// raise stack limit, feature of v8/node.js
Error.stackTraceLimit = 100;


function c() {
    var MongoClient = require("mongodb").MongoClient;
    return MongoClient.connectAsync('mongodb://non-existent host/')
}

function b() {
    return c()
}

function a() {
    return b()
}

a().then(function(connection) {

});

给予:

Possibly unhandled Error: failed to connect to [non-existent host:27017]
    at null.<anonymous> (/home/petka/bluebird/node_modules/mongodb/lib/mongodb/connection/server.js:546:74)
    at EventEmitter.emit (events.js:106:17)
    at null.<anonymous> (/home/petka/bluebird/node_modules/mongodb/lib/mongodb/connection/connection_pool.js:150:15)
    at EventEmitter.emit (events.js:98:17)
    at Socket.<anonymous> (/home/petka/bluebird/node_modules/mongodb/lib/mongodb/connection/connection.js:533:10)
    at Socket.EventEmitter.emit (events.js:95:17)
    at net.js:830:16
From previous event:
    at Function.connectAsync (eval at makeNodePromisifiedEval (/home/petka/bluebird/js/main/promisify.js:199:12), <anonymous>:7:21)
    at c (/home/petka/bluebird/throwaway.js:10:28)
    at b (/home/petka/bluebird/throwaway.js:14:16)
    at a (/home/petka/bluebird/throwaway.js:18:16)
    at Object.<anonymous> (/home/petka/bluebird/throwaway.js:21:5)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:902:3

您可以在一个地方使用 catch(之所以这样命名,是因为它像真正的 catch 语句一样工作):

 a().catch(function(e) {
      //handle e
 });

还添加了蓝鸟特定功能来捕捉:

还支持谓词捕获,因为它只是一种方法:

 a().catch(SyntaxError, function(e) {

 });

谓词可以是错误构造函数或谓词函数

 // define a predicate for IO errors
 function IOError(e) {
     return "code" in Object(e);  
 }

【讨论】:

  • 我认为你的图书馆的这个“来自以前的活动”功能是你应该在这里做广告的。 OP 抱怨手动必须在任何地方写出log("something went wrong at …"),不是吗?
  • @Bergi 同意,我会删除日志版本
  • 您可能还想强调这不是 Promise 的固有属性,而是某些库的实现特性。
  • @Bergi 哦,是的,我注释了蓝鸟特定的东西
【解决方案2】:

如何从错误中使用堆栈跟踪:

function trace(err)
{
    console.log(err);
    var stack = new Error().stack;
    console.log(stack);
}


a(function (err, mongo) {
    if (err)
    trace(err)
});

function a(callback) {
    b(function (err, mongo) {
        if (err)
        return callback(err);
        else
        return callback(null, mongo);
    });
}

function b(callback) {
    c(function (err, mongo) {
        if (err)
        return callback(err);
        else
        return callback(null, mongo);
    });
}

function c(callback) {
    var MongoClient = require('mongodb').MongoClient;
    return MongoClient.connect('mongodb://nohost/', function (err, mongo) {
        if (err)
        return callback(err);
        else
        return callback(null, mongo);
    });
}

输出

[Error: failed to connect to [non-existent host:27017]]
Error
    at trace (/myfolder/a.js:4:14)
    at /myfolder/a.js:11:2
    at /myfolder/a.js:17:10
    at /myfolder/a.js:26:10
    at /myfolder/a.js:36:10
    at /myfolder/node_modules/mongodb/lib/mongodb/mongo_client.js:406:11
    at process._tickCallback (node.js:339:11)

现在它不像你在 Java 中得到的那样冗长,它没有给出函数名称,但它有助于识别执行链。

【讨论】:

  • ...除非您想要在堆栈跟踪中使用的方法(abc)使用高阶函数来生成回调。在这种情况下,您只会获得生成器函数的行号。
【解决方案3】:

longjohn 现在似乎很流行,因为它可以在 node.js 中获取长堆栈跟踪

http://blog.nodejitsu.com/npmawesome-full-stack-traces-with-longjohn/

http://www.mattinsler.com/post/26396305882/announcing-longjohn-long-stack-traces-for-node-js

这里列出了可能有助于确定您需要什么的其他此类模块

https://nodejsmodules.org/tags/stack

示例代码:

var longjohn = require('longjohn')
var stackTrace = require('stack-trace');
longjohn.empty_frame = 'ASYNC CALLBACKS :';
a();

function a() {

    b();
};

function b() {
    c();
}

function c() {

    setTimeout(function() {

        throw new Error("My Custom Error");

    }, 1000);

}

process.on('uncaughtException', function(err) {

    //in JSON format with the help of stack-trace module;
    console.error(stackTrace.parse(err));

    //in plain text format
    console.error(err.stack.trim());

    process.exit(1);

});

使用 longjohn 以纯文本格式输出:

Error: My Custom Error
    at [object Object].<anonymous> (/Users/nav/Documents/workspace/stack_trace/hello-world-server.njs:19:9)
    at listOnTimeout (timers.js:110:15)
ASYNC CALLBACKS :
    at c (/Users/nav/Documents/workspace/stack_trace/hello-world-server.njs:17:2)
    at b (/Users/nav/Documents/workspace/stack_trace/hello-world-server.njs:12:2)
    at a (/Users/nav/Documents/workspace/stack_trace/hello-world-server.njs:8:2)
    at Object.<anonymous> (/Users/nav/Documents/workspace/stack_trace/hello-world-server.njs:4:1)
    at Module._compile (module.js:456:26)
    at Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Module._load (module.js:312:12)

没有longjohn的纯文本格式输出:

Error: My Custom Error
    at null._onTimeout (/Users/nav/Documents/workspace/stack_trace/hello-world-server.njs:19:9)
    at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)

【讨论】:

    猜你喜欢
    • 2016-12-04
    • 2018-08-05
    • 1970-01-01
    • 1970-01-01
    • 2014-01-23
    • 1970-01-01
    • 2014-12-10
    • 2016-12-13
    • 2011-05-06
    相关资源
    最近更新 更多