#Nestjs controller vs forwardRef issue

8 messages · Page 1 of 1 (latest)

unborn grail
#

Is there an existing issue for this?
I have searched the existing issues
Current behavior
This issue will happen with the bellow condition

The controller inject service A
Service A has using forwardRef for inject service B
Service A constructor call the async function for modified the service A property
=> From the controller, we can't access the latest value of that property. The value has been cached.
Minimum reproduction code
https://github.com/khanhx/nestjs-controller-forwardref-bug

Steps to reproduce
npm ci
npm run start
image
Expected behavior
The value from controller should match with value in the service A since we only have one instance of service A

Package
I don't know. Or some 3rd-party package
@nestjs/common
@nestjs/core
@nestjs/microservices
@nestjs/platform-express
@nestjs/platform-fastify
@nestjs/platform-socket.io
@nestjs/platform-ws
@nestjs/testing
@nestjs/websockets
Other (see below)
Other package
No response

NestJS version
No response

Packages versions
"@nestjs/common": "^10.3.2",
"@nestjs/core": "^10.3.2",
"@nestjs/platform-express": "^10.3.2",
"reflect-metadata": "^0.2.1",
"rxjs": "^7.8.1"
Node.js version
v18.18.2

In which operating systems have you tested?
macOS
Windows
Linux
Other
No response

https://github.com/nestjs/nest/issues/13826

Can anyone help me with this, It seem to be not related to any problem before. The code working as we have 2 instance of service but the constructor only call once

GitHub

Contribute to khanhx/nestjs-controller-forwardref-bug development by creating an account on GitHub.

GitHub

Is there an existing issue for this? I have searched the existing issues Current behavior This issue will happen with the bellow condition The controller inject service A Service A has using forwar...

flat onyx
#

This seems strange to me. Does it work correctly when you remove the forward ref?

#

If you haven't solved it yet, I'll try to run your repro when I have time

flat onyx
#

I also couldn't find any issue where this was discussed in the past like Kamil said 🤷‍♂️

solemn marsh
#

Your code doesn't make any sense...
You re-assign the instanceKey value and log it to the console at the random states of lifecycle...

  1. First of all kicks you AppService at the line 9 with your first value assign instanceKey = Math.random(); on module init... Which then you log it inside constructor line 11
  2. Then your constructor execut this code
    setTimeout(() => {
      console.log(333333, 'AppService current value', this.instanceKey);
    }, 3000);
    this.init();

Which first you see the this.init(); value that you re-assign on line 21 this.instanceKey = Math.random(); with the new value.

And you see it first sinse your setTimeout function set to 1 second.
Then you see the first part of your constructor:

    setTimeout(() => {
      console.log(333333, 'AppService current value', this.instanceKey);
    }, 3000);

Because you set it to 3 seconds later with the same value from line 21 (check out the sreenshot)

  1. Finally you controller kicks in
  constructor(
    @Inject(forwardRef(() => AppService))
    private appService: AppService,
  ) {
    setTimeout(() => {
      console.log(11111, 'AppController current value', this.appService?.instanceKey);
    }, 3000);
  }

with delay of 3 seconds after private appService: AppService, initialized and constructor executed.

Basically nothing in your code is "async", especially the constructor that cannot be async.

So everything work as expected.

#

Under the hood, when you use setTimeout function, nothing is going to nodejs libuv (the library that makes your code async), you push the values (the callbacks) to event-loop in sorted order by time. Then the event-loop execute it one-by-one.

flat onyx
#

If I'm not completely mistaken, the callback passed to setTimeout should execute after the timeout has passed, which means the variable should be read 2 seconds after it had been changed (which is 1 second after init), so we should see the new value in the constructor. But we don't.