"Pila de llamadas" para callbacks en node.js

Estoy acostumbrado a pensar en Java y estoy tratando de entender a node.js. Mi progtwig necesita registrar información cuando las cosas van mal, y me parece que tengo que poner un montón de código repetitivo en mi progtwig node.js para get lo que obtendría gratis en Java.

Mi pregunta se networkinguce a:

  • ¿Existe una forma más fácil / no repetitiva de get información similar a la stack en una cadena de devoluciones de llamadas? y / o
  • ¿Soy culpable de no entender bien node.js y de forzar que node.js asynchronous se parezca más a Java sincrónico?

Ejemplo de Java

Aquí hay un progtwig de Java noddy que intenta (y falla) conectarse a una database de Mongo: 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"); } } 

… lo que le da a esta útil salida de stack:

 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) 

(En particular, las últimas 4 líneas me muestran "lo que estaba sucediendo" en mi propio código en el momento en que ocurrió el error de Mongo).

Ejemplo de Node.js

Aquí está mi bash de volver a escribir mi progtwig en 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); }); } 

… que da esta salida:

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

Pero para get esta salida, tengo que poner un montón de código repetitivo a lo largo de mi progtwig, lo cual será un fastidio para la policía ya que mi progtwig se hace más grande y tengo todo un equipo de desarrollo.

¿Puedo get esta salida similar a una stack de otra manera? ¿Es un nodo similar esperar este tipo de salida?

Las promises son exactamente lo que estás buscando (trae las características de la stack al código asíncrono)

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

Da:

 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 

Puede usar catch (así se llama porque funciona como una statement catch real) en un solo lugar:

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

También características específicas de Bluebird agregadas para atrapar:

Las capturas previstas también son compatibles, ya que es solo un método:

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

El pnetworkingicado puede ser un constructor de error o una function de pnetworkingicado

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

¿Qué tal si usamos el rastro de stack del error?

 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); }); } 

Salida

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

Ahora no es tan detallado como el que tienes en Java, no da nombres de funciones, pero ayuda a identificar la cadena de ejecución.

longjohn parece ser muy popular hoy en día para get trazas de stack largas en 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

Aquí hay una list de otros modules similares que pueden ser útiles para identificar lo que necesita

https://nodejsmodules.org/tags/stack

Código de ejemplo:

 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); }); 

Salida en formatting de text plano con 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) 

Salida en formatting de text plano sin 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)