循环依赖如何解决

Nest 实现了一套模块系统,可以通过 imports 引用其他模块。

如果 Module 和 Module 互相引用,形成循环依赖怎么办?

现在我们创建两个 module:

bash
1nest g module aaa
2nest g module bbb

然后让两个 module 互相引用:

ts
1import { Module } from '@nestjs/common'
2import { BbbModule } from 'src/bbb/bbb.module'
3
4@Module({
5  imports: [BbbModule],
6})
7export class AaaModule {}
ts
1import { Module } from '@nestjs/common'
2import { AaaModule } from 'src/aaa/aaa.module'
3
4@Module({
5  imports: [AaaModule],
6})
7export class BbbModule {}

执行pnpm run start:dev,nest 会直接报错:

image-20231014182834415

由于 nest 启动时会递归 BbbModule 的依赖(AaaModule),而 AaaModule 又依赖了 BbbModule,造成了循环依赖,所以没办法创建成功,拿到的就是 undefined。

nest 的解决的方法是单独创建这两个依赖,然后让它们关联起来。

根据提示,我们需要使用 forwardRef。

ts
1@Module({
2  imports: [forwardRef(() => BbbModule)],
3})
ts
1@Module({
2  imports: [forwardRef(() => AaaModule)],
3})

nest 会单独创建两个 Module,之后再把 Module 的引用转发过去,也就是 forwardRef 的含义。

img

除了 Module 之间会有循环依赖之外,provider 也会有循环依赖的问题。

创建两个 service:

bash
1nest g service ccc
2nest g service ddd

然后互相引用:

ts
1@Injectable()
2export class CccService {
3  constructor(private readonly dddService: DddService) {}
4  ccc: () => 'ccc'
5}
ts
1@Injectable()
2export class DddService {
3  constructor(private readonly cccService: CccService) {}
4  ddd: () => 'ddd'
5}

同样会报错:

image-20231014190428065

然后用 forwardRef 单独创建依赖:

ts
1@Injectable()
2export class CccService {
3  @Inject(forwardRef(() => DddService))
4  private readonly dddService: DddService
5  ccc: () => 'ccc'
6}
ts
1@Injectable()
2export class DddService {
3  @Inject(forwardRef(() => CccService))
4  private readonly cccService
5  ddd: () => 'ddd'
6}

报错消失了:

image-20231014190454879

总结

Module 之间可以相互 imports,Provider 之间可以相互注入,这两者都会形成循环依赖。

解决方式就是两边都用 forwardRef 来包裹下。

它的原理就是 nest 会先创建 Module、Provider,之后再把引用转发到对方,也就是 forward ref。