Skip to content

tracingChannel.tracePromise forces native promises聽#59936

Description

@bizob2828

Version

18.19.0+

Platform

All

Subsystem

diagnostics_channel

What steps will reproduce the bug?

We received a report about an openai method crashing that's been wrapped with tracingChannel.tracePromise. After some digging, I see the issues is here. OpenAI creates a custom promise and it's getting stripped in tracePromise. This is a distilled repro case to show the issue.

import assert from 'node:assert';
import { tracingChannel } from 'node:diagnostics_channel';
const channel = tracingChannel('custom-promise')
channel.subscribe({
  asyncStart(data) {
  }
})

class CustomPromise {
    constructor(executor) {
        this.state = 'pending';  // Possible states: 'pending', 'fulfilled', 'rejected'
        this.value = undefined;  // Will hold the result or error
        this.successCallbacks = [];
        this.errorCallbacks = [];

        // Executor is the function passed to the promise
        try {
            executor(this._resolve, this._reject);
        } catch (error) {
            this._reject(error);
        }
    }

    // Custom resolve function
    _resolve = (value) => {
        if (this.state === 'pending') {
            this.state = 'fulfilled';
            this.value = value;
            this.successCallbacks.forEach(callback => callback(this.value));
        }
    }

    // Custom reject function
    _reject = (error) => {
        if (this.state === 'pending') {
            this.state = 'rejected';
            this.value = error;
            this.errorCallbacks.forEach(callback => callback(this.value));
        }
    }

    // Then method to handle successful promise resolution
    then(successCallback) {
        if (this.state === 'fulfilled') {
            successCallback(this.value);
        } else if (this.state === 'pending') {
            this.successCallbacks.push(successCallback);
        }
        return this;  // Allows chaining 馃攧
    }

    // Catch method to handle promise rejection
    catch(errorCallback) {
        if (this.state === 'rejected') {
            errorCallback(this.value);
        } else if (this.state === 'pending') {
            this.errorCallbacks.push(errorCallback);
        }
        return this;  // Allows chaining 馃攧
    }
}

function test(arg) {
  return new CustomPromise((resolve) => {
    setTimeout(() => {
      resolve(arg)
    }, 100)
  }) 
}

const arg = 'test'
const promise = channel.tracePromise(test, { ctx: true}, this, arg)
assert.equal(promise.constructor.name, 'CustomPromise')
const result = await promise
assert.equal(result, arg)

How often does it reproduce? Is there a required condition?

Every time

What is the expected behavior? Why is that the expected behavior?

To properly return the custom promise

What do you see instead?

It returns a native Promise instead

Additional information

I could workaround this by using traceSync and propagating the promise myself, but I'd prefer that the API does the right thing.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    diagnostics_channelIssues and PRs related to the diagnostics_channel module.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions