Home > database >  Node - How to reconstruct full stack trace for Promise rejection
Node - How to reconstruct full stack trace for Promise rejection

Time:06-22

I've stumbled upon an issue similar to the one discussed in this question but does not seem to be the same. I have a function returning a promise and rejecting (in case of errors) after a while (it's doing an HTTP call, so it takes some time). While the request is flying around the internet Node takes care of other instructions, when the request is received and the error thrown the stack trace begins from processTicksAndRejections (as it would for any async operations, I guess). This is a problem as I no longer have a record of where the function was called from.
So I tried the following approach, the normal code would be

const fetch = require('node-fetch');

function myFunction() {
  return new Promise(async (resolve, reject) => {
    fetch('https://www.google.com').then(() => {
      return reject(new Error('Whoops!'));
    });
  });
}

myFunction().catch(console.log);

which prints out

Error: Whoops!
    at /home/dami/projects/testing/stack.js:8:21
    at processTicksAndRejections (internal/process/task_queues.js:95:5)

The idea is to concatenate the old stack trace like

function myFunction() {
  const stack = (new Error()).stack;

  return new Promise(async (resolve, reject) => {
    fetch('https://www.google.com').then(() => {
      const error = new Error('Test');
      error.stack  = stack;
      reject(error);
    });
  });
}

myFunction().catch(console.log);

which prints

Error: Test
    at /home/dami/projects/testing/stack.js:8:21
    at processTicksAndRejections (internal/process/task_queues.js:95:5)Error
    at myFunction (/home/dami/projects/testing/stack.js:4:18)
    at a (/home/dami/projects/testing/stack.js:16:3)
    at Object.<anonymous> (/home/dami/projects/testing/stack.js:19:1)
    at Module._compile (internal/modules/cjs/loader.js:1085:14)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
    at Module.load (internal/modules/cjs/loader.js:950:32)
    at Function.Module._load (internal/modules/cjs/loader.js:790:12)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:76:12)
    at internal/main/run_main_module.js:17:47

However, this does not seem the best (and most neat) solution to me, as at this point I have two 'sources' of the trace (one being the normal Node process at run_main_module, the other at processTicksAndRejections).
So the final question, is there a best practice approach to this problem? I've tried using async/await and keeping the rejection at the top level of myFunction, but it will always be picked up by processTicksAndRejections because of its async nature. Thanks!

CodePudding user response:

I would use an async function here exactly for the reason of preserving the stack trace:

async function myFunction() {
    await fetch('https://www.google.com');
    throw new Error('Whoops!');
}

async function foo() {
    await myFunction();
}

async function bar() {
    await foo();
}


bar().catch(console.log);

Don't be bothered by the process.processTicksAndRejections entry in the stack trace. As long as async/await are used consistently, the whole call chain should be logged with the exception.

  • Related