Is there a clean way of executing a function immediately after an RxJs subscription is set up?

Viewed 83

What I want to do:

  1. Send a 'request' event to a websocket server.
  2. Receive an immediate response event bundled with some additional data.
  3. Keep receiving responses over time.

My question is:

  • Is there a cleaner way of doing what I'm doing below? Without using setTimeout.

First, look at this simplified working example:

const { Subject, defer, interval, of} = rxjs;
const EventEmitter = EventEmitter3;


const emitter = new EventEmitter();
const subject = new Subject();

// The next lines mock a websocket server message listener, imagine this being present on server side
emitter.on("request", () => {
  subject.next(`Immediate response`);
  interval(1000).subscribe((index) =>
    subject.next(`Delayed response...${index}`)
  );
});



// Imagine the following code being present in the browser
function getData() {
  return defer(() => {
    // The next line mocks a websocket client sent event
    setTimeout(() => emitter.emit("request"), 0);
    return subject;
  });
}

getData().subscribe((data) => console.log(data));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.min.js"></script>
<script src="https://unpkg.com/eventemitter3@latest/umd/eventemitter3.min.js"></script>

Now look at a non-working example. Here, the client doesn't get the immediate response, because the 'request' event is being sent before the subscription is set up.

const { Subject, defer, interval, of} = rxjs;
const EventEmitter = EventEmitter3;


const emitter = new EventEmitter();
const subject = new Subject();

// The next lines mock a websocket server message listener, imagine this being present on server side
emitter.on("request", () => {
  subject.next(`Immediate response`);
  interval(1000).subscribe((index) =>
    subject.next(`Delayed response...${index}`)
  );
});


// Imagine the following code being present in the browser
function getData() {
  return defer(() => {
    emitter.emit('request');
    return subject;
  });
}

getData().subscribe((data) => console.log(data));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.min.js"></script>
<script src="https://unpkg.com/eventemitter3@latest/umd/eventemitter3.min.js"></script>

1 Answers

You could use a BehaviorSubject instead of a Subject:

const { BehaviorSubject, defer, interval, of } = rxjs;
const EventEmitter = EventEmitter3;


const emitter = new EventEmitter();
const subject = new BehaviorSubject();

// The next lines mock a websocket server message listener, imagine this being present on server side
emitter.on("request", () => {
  // the timing of this synchronous response would be impossible in a real socket
  subject.next(`Immediate response`);
  interval(1000).subscribe((index) =>
    subject.next(`Delayed response...${index}`)
  );
});


// Imagine the following code being present in the browser
function getData() {
  return defer(() => {
    emitter.emit('request');
    return subject;
  });
}

getData().subscribe((data) => console.log(data));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.min.js"></script>
<script src="https://unpkg.com/eventemitter3@latest/umd/eventemitter3.min.js"></script>

Keep in mind though that it's impossible for the server's response to arrive back synchronously after the socket request is sent in the same way you've implemented your mock for this demo, so while Subject does not produce the expected result here, it would work fine with a real socket.

Related