ReactiveX / rxjs

A reactive programming library for JavaScript
https://rxjs.dev
Apache License 2.0
30.83k stars 3.01k forks source link

lost stacktrace after promise is resolved #7442

Closed ChoSeoHwan closed 9 months ago

ChoSeoHwan commented 9 months ago

Describe the bug

Lost stacktrace after promise is resolved.

$ yarn start
============================== step_1 ==============================
Error
    at Object.next (file:///C:/Projects/rxjs-bug-report-0/src/main.js:13:25)
    # .....
    at file:///C:/Projects/rxjs-bug-report-0/src/main.js:25:1
    at ModuleJob.run (node:internal/modules/esm/module_job:218:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
    at async loadESM (node:internal/process/esm_loader:28:7)
    at async handleMainPromise (node:internal/modules/run_main:113:12)
============================== step_2 ==============================
Error
    at Object.next (file:///C:/Projects/rxjs-bug-report-0/src/main.js:18:25)
    # lost stack trace
finish

Expected behavior

Expect

Stacktrace must be maintained after promise is resolved.

$ yarn start
============================== step_1 ==============================
Error
    at Object.next (file:///C:/Projects/rxjs-bug-report-0/src/main.js:13:25)
    # .....
    at file:///C:/Projects/rxjs-bug-report-0/src/main.js:25:1
    at ModuleJob.run (node:internal/modules/esm/module_job:218:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
    at async loadESM (node:internal/process/esm_loader:28:7)
    at async handleMainPromise (node:internal/modules/run_main:113:12)
============================== step_2 ==============================
Error
    at Object.next (file:///C:/Projects/rxjs-bug-report-0/src/main.js:18:25)
    # .....
    at file:///C:/Projects/rxjs-bug-report-0/src/main.js:25:1
    at ModuleJob.run (node:internal/modules/esm/module_job:218:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
    at async loadESM (node:internal/process/esm_loader:28:7)
    at async handleMainPromise (node:internal/modules/run_main:113:12)
finish

Reproduction code

Error.stackTraceLimit = Infinity;

import { of, mergeMap, tap } from 'rxjs';

const delay = (timeout) => new Promise((resolve) => {
    setTimeout(resolve, timeout);
})

const start = () => {
    of(null).pipe(
        tap(() => {
            console.log('============================== step_1 ==============================');
            console.log(new Error().stack);
        }),
        mergeMap(async () => await delay(1000)),
        tap(() => {
            console.log('============================== step_2 ==============================');
            console.log(new Error().stack);
        }),
    ).subscribe(() => {
        console.log('finish');
    });
}

start();

Reproduction URL

https://github.com/ChoSeoHwan/rxjs-bug-report-0

Version

7.8.1

Environment

Additional context

No response

benlesh commented 9 months ago

Everything that happens after the promise is resolved (when the microtask fires) happens synchronously in nested functions, and is no different than if you were to do something like:

(async () => {
  await delay(1000)
  console.log(new Error().stack);
})()

Or more accurately:

const delay = (timeout) => new Promise((resolve) => {
    setTimeout(resolve, timeout);
});

(async function mergeMapNext() {
  await delay(1000);
  rxjsLogic();
})();

function rxjsLogic() {
  tapNext();
}

function tapNext() {
  innerTapNext();
}

function innerTapNext() {
  userProvidedFunction();
}

function userProvidedFunction() {
  console.log(new Error().stack);
}

That said, I couldn't replicate the issue myself: https://replit.com/@benlesh/rxjs-issue-7442#index.js

And I also tried cloning your repository and checking on the same version of node. I couldn't reproduce the issue.

I would recommend that you look up "why is my stack trace missing in node?" because it seems to be a condition of errors being thrown after promise resolution in specific versions of node? Although I tried the same one and could reproduce the bug.

Regardless, this doesn't seem to have anything to do with RxJS, because we can't really control the call stack produced by new Error().stack.