"Call stack" for callbacks in node.js

Renny Barrett

I'm used to thinking in Java and I'm trying to get my head around node.js. My program needs to log information when things go wrong, and I find I'm having to put in a lot of boilerplate code in my node.js program to get what I'd get for free in Java.

My question boils down to:

  • is there an easier/non-boilerplate way to get stack-like information in a chain of callbacks? and/or
  • am I guilty of failing to grasp node.js properly, and trying to force asynchronous node.js to be more like synchronous Java?

Java Example

Here's a noddy Java program which tries (and fails) to connect to a Mongo database: import 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");
    }

}

...which gives this helpful stack output:

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)

(In particular, the last 4 lines show me "what was happening" in my own code at the time the Mongo error occurred.)

Node.js Example

Here's my attempt to re-write my program in 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);
    });
}

...which gives this output:

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]]

But to get this output, I have to put in lots of boilerplate code throughout my program, which is going to be a pain to police as my program gets larger and I have a whole development team.

Can I get this stack-like output another way? Is it un-node-like to expect this kind of output?

Esailija

Promises are exactly what you are looking for (bring back the stack features to async code)

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) {

});

Gives:

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

You can use catch (named so because it works like a real catch statement) in one place:

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

Also bluebird specific features added to catch:

Predicated catches are also supported since it's just a method:

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

 });

Predicate can be an error constructor or a predicate function

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

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

"Call stack" for callbacks in node.js

From Dev

RangeError: Maximum call stack size exceeded for Node.js

From Dev

node.js call stack exceeded with sockets.io

From Dev

Node JS Callbacks

From Dev

JS Call Stack with context

From Dev

Loops with callbacks in node.js

From Dev

Node js callbacks in exported modules

From Dev

Node js callbacks in exported modules

From Dev

Node JS Mongoose Async Callbacks

From Dev

JS maximum call stack exceeded

From Dev

Node.js + Socket.io Maximum call stack size exceeded

From Dev

node.js \ why do I get RangeError: Maximum call stack size exceeded

From Dev

Node.js — Maximum call stack size exceeded, even with process.nextTick()

From Dev

Need another way to compare strings node.js if else statement crashes the call stack

From Dev

Node loop: Maximum call stack size exceeded

From Dev

Node.js Control Flow and Callbacks

From Java

Replacing callbacks with promises in Node.js

From Dev

basic node.js callbacks assigned to variable

From Dev

Getting a better understanding of callbacks with Node.js

From Dev

Waiting for multiple callbacks in Node.js

From Dev

Node.js promises, async, or just callbacks

From Dev

sqlite3 callbacks (node.js)

From Dev

How to create asynchronous callbacks in node js?

From Dev

Problems with multiple Node.js callbacks

From Dev

Understanding node.js callbacks 2.0

From Dev

Node.js Express - next() callbacks

From Dev

understanding REALLY basic callbacks in node / js

From Dev

Node.js Control Flow and Callbacks

From Dev

Javascript / node.js - callbacks inside Promise