Refactoring for Asynchronous Initialization in Effect-Based Codebase

Hey everyone! 👋

I'm working on introducing an Effect into an existing class-based codebase and could use some guidance. My current approach involves adding a SubscriptionRef to UserSession, like this:
class UserSession {
  connected = Effect.runSync(SubscriptionRef.make(false))
  unsub!: () => void;

  constructor() {
    const listenChanges = this.connected.pipe(
      // do things
      Stream.runDrain
    )
    const fiber = Effect.runFork(listenChanges);
    this.unsub = () => Effect.runSync(Fiber.interruptFork(fiber))
  }

  onDestroy() {
    this.unsub();
  }
}


However, in some comments, Michael mentioned that we shouldn’t run synchronous effects (runSync) if they might suspend. While exploring the effect codebase, I noticed that SubscriptionRef relies on PubSub, which is created with suspend.

So, here’s my question: do I need to refactor my original code to something like this for it to work correctly?

class UserSession2 {
  connected!: SubscriptionRef.SubscriptionRef<boolean>;
  unsub!: () => void;

  static async asyncMake() {
    const user = new UserSession();
    user.connected = await Effect.runPromise(SubscriptionRef.make(false));
    const listenChanges = user.connected.pipe(
      // do things
      Stream.runDrain
    )
    const fiber = Effect.runFork(listenChanges);
    user.unsub = () => Effect.runSync(Fiber.interruptFork(fiber));
    return user;
  }

  onDestroy() {
    this.unsub();
  }
}


Additionally, could someone explain why creating things like PubSub or Queue might cause suspension? Would it be possible to work around this by having .unsafeMake methods for these primitives?

Thanks a lot!
Was this page helpful?