Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Await all "response" listeners to finish #569

Open
mdesousa opened this issue Apr 29, 2024 · 4 comments
Open

Await all "response" listeners to finish #569

mdesousa opened this issue Apr 29, 2024 · 4 comments

Comments

@mdesousa
Copy link
Contributor

hi, we have code that looks like this:

const interceptor = new BatchInterceptor(...); // console.log to print requests and responses
const p1 = fetch('x.com');
const p2 = fetch('y.com');
await Promise.all([p1, p2]);

if there is an error with one of the requests, the process ends
in this scenario, we would like to make sure that responses are logged. however, there may be a small delay and the process terminates before the response is logged.

is there a way to wait until the interceptor has invoked the response hook for all pending requests?

@kettanaito
Copy link
Member

Hi 👋

if there is an error with one of the requests, the process ends

That happens because you use Promise.all() though. Consider Promise.allSettled() if you expect request failures as well.

however, there may be a small delay and the process terminates before the response is logged.

Am I reading this right: the process exists before the "response" listeners you have are called?

The response event is emitted as soon as the response starts streaming. If your logic doesn't get called until the response finishes, I suspect you are doing something async in the response listener. Is that the case?

Overall, I don't think we should provide any means to await events as that's outside of tihs library's promise. But I'd like to learn more about your use case and see if I have something to recommend to achieve it.

@kettanaito kettanaito changed the title is there a way to wait for all responses to be called? Await all "response" listeners to finish Apr 29, 2024
@kettanaito
Copy link
Member

Right now, we just emit the response event:

This means if the listener to that event is async, the process will not await its execution.

We can use the emitAsync() utility for the response event as well. That utility awaits all the listeners, even if they are async, to finish before freeing the event loop.

There are a couple of downsides to using it:

  1. It will be a part of the request resolution, so it will affect the request time.
  2. In some cases, like in NodeClientRequest, it will have no effect at all since ClientRequest.prototype.end must be synchronous. And there's nothing else to defer to the emitAsync() promise's completion.

@mdesousa
Copy link
Contributor Author

hi @kettanaito thanks for your answer!
you are correct, we are doing something async in the response listener. we are trying to log details about there response... and reading the body requires that we call await response.text(). see simplified code below:

  interceptor.on("response", async ({ response, requestId }) => {
    const { status } = response;
    const x = response.clone();
    const body = await x.text();
    console.debug(`res-${requestId}`, { status, body });
  });

below is an example to test the issue. when you run this, you'll see the "response completed" line prints before the console.debug called by the response listener. in cases where the process exists or throws before the response listener completes, nothing is logged. it would be nice if we had a way to wait for all listeners for the interceptor to settle.

const res = await fetch("https://cdn2.thecatapi.com/images/dh7.jpg");
console.log("response completed", res.status);
// if (!res.ok) throw... response body never gets logged

@kettanaito
Copy link
Member

Yeah I think we have to await the response listeners. If you have none or all are sync, you pay nothing. If it's async, you expect that async to resolve within the request promise anyway.

Should be a matter of adding tests and swapping this.emitter.emit with emitAsync.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants