0

I am using this <res.write()> ==> https://nodejs.org/api/http.html#responsewritechunk-encoding-callback (in nodejs) and using this fetch ==> https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

My situation is that I didn't see any response when using the res.write() function inside of the fetch function. for example, in the below backend code, I tried to put res.write("456") inside of the first then function below fetch, but I only see 123 returned in the frontend, no 456.

res.write("123");
fetch('http://example.com/movies.json')
  .then((response) => {response.json(), res.write("456")})
  .then((data) => console.log(data));

I have searched Google for a while, but didn't see anything related. My guess is that this could be because of async usage. appreciate if someone can give suggestions.

===== update =====

res is express's res obj

async sendText(res: Response){
    res.write("123")
    fetch('http://example.com/movies.json')
        .then((response) => {return response.json()})
        .then((data) => {
            console.log(data);
            res.write('456');
            res.end();
        });

}

seeing behavior: only see 123 in the frontend.

VS

async sendText(res: Response){
    res.write("123")
    await fetch('http://example.com/movies.json')
        .then((response) => {return response.json()})
        .then((data) => {
            console.log(data);
            res.write('456');
            res.end();
        });

}

seeing behavior: can see both 123 and 456 in the frontend.

I never use await and .then together before, not fully understand the difference. searching the web rn.

Phil
  • 157,677
  • 23
  • 242
  • 245
Tony Lucas
  • 189
  • 1
  • 13
  • 1
    You need to use `res.end()` when you're done writing everything. See [difference between res.send and res.write](https://stackoverflow.com/questions/44692048/what-is-the-difference-between-res-send-and-res-write-in-express) – Barmar Jan 12 '23 at 22:13
  • your first `.then` callback doesn't return anything, so `data` will be undefined. – CollinD Jan 12 '23 at 22:14
  • 1
    Your second approach (`res.end()` after `fetch`) definitively won't work, because `res.end()` would be called *before* the fetch is finished ... Maybe if you show your actual code, and explain what you actually want to do, we could help you better. Because, I somehow assume, the result of this `fetch` should be part of the response to the client? If that's the case, you have to `await response.json()` because that's a promise as well ... – derpirscher Jan 12 '23 at 22:31
  • Why are you using the [comma operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator) and not returning anything from your first `.then()` callback? – Phil Jan 12 '23 at 22:32
  • 1
    Furthermore you have to consider what happens, if `fetch` or `response.json()` throws an exception. THen none of your approaches will ever reach the `res.write("456")` – derpirscher Jan 12 '23 at 22:39
  • FYI, `fetch()` does not give you partial responses. When you do `response.json()`, it waits for the entire response to be delivered before giving you anything. So, you will have to `.end()` the response before `fetch()` will return anything. – jfriend00 Jan 12 '23 at 22:48
  • Plus, something is funky here. `res.write()` must be done to the `res` from an already existing request. You can't then do a `fetch()` and somehow access that same request. `fetch()` starts a NEW request that will need it's own `res.write()` and `res.end()` or just `res.send()` on the server. I'd suggest you show a larger section of working code so we can see what's really going on here. You can never do `res.write()` before a `fetch()` because before the `fetch()`, the `res` for that new request doesn't exist yet. – jfriend00 Jan 12 '23 at 22:50
  • @jfriend00 I think `res` is just the usual Express `Response`. OP has the `fetch` response in `response` – Phil Jan 12 '23 at 22:52
  • @Phil - That's not very clear here. This question is currently unclear and ambiguous and not a sufficient [Minimal, Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example) and needs a bunch of clarification. Regardless, that `res` would belong to some DIFFERENT request than the one that `fetch()` is about to create since the `res` that corresponds to that `fetch()` doesn't get created until AFTER `fetch()` is called. – jfriend00 Jan 12 '23 at 22:59
  • @jfriend00 OP says `res` is [http.ServerResponse](https://nodejs.org/api/http.html#http_class_http_serverresponse) and it appears to defined before the fetch request even begins given they said they can see the "123" in the response (to whatever this is) – Phil Jan 12 '23 at 23:01
  • @Phil - Yeah, so that `res` would have nothing at all to do with the `fetch()` that comes after it. – jfriend00 Jan 12 '23 at 23:05
  • @jfriend00 that's right but it's still the same `res` in the `.then()` callbacks – Phil Jan 12 '23 at 23:06
  • 1
    With the update you've added, there is literally no difference to the handling of `res.write()`. The only difference would be how errors are handled in whatever calls `sendText()`; the first returns a promise that **always resolves** with `undefined`. The second returns a promise that may either resolve or reject – Phil Jan 12 '23 at 23:33
  • @Phil then how is it possible that adding the await before the fetch can make res.write() send 456? That's what I saw. – Tony Lucas Jan 12 '23 at 23:45
  • 1
    That entirely depends on what calls `sendText()` and how errors are handled – Phil Jan 12 '23 at 23:50
  • 2
    There is probably some additional code that calls `sendText()`. And in this code there might be something that causes a `res.end()` (either explicitly or implicitly). So when the `fetch` inside `sendText` is awaited it reaches the `res.end()` in there. If not, the "outside" `res.end()` is called before the `fetch` resolves, thus `res.write("456")` does not have any effect anymore (or even causes an error ...) However it may be, there are too many uncertainties in this question to be able to answer it ... – derpirscher Jan 13 '23 at 09:09
  • @derpirscher, thanks, I think your answer is the clearest and addresses my question directly. I used a nodeJS framework as backend, I didn't write additional code that calls ```sendText()```, just used it inside of a normal controller. The framework, however, does something for me (I don't have control on this): as you said, there is probably something that implicitly calls ```res.end()``` before ```fetch``` resolves. – Tony Lucas Jan 16 '23 at 23:02

1 Answers1

1

You aren't checking for any errors or an unsuccessful response so response.json() may be failing, preventing anything after it from executing.

Something like this should work better for you

async sendText (res: Response) {
  res.write("123");

  const response = await fetch("http://example.com/movies.json");

  // check for an unsuccessful response
  if (!response.ok) {
    const error = new Error(`${response.status} ${response.statusText}`);
    error.text = await response.text();
    throw error;
  }

  const data = await response.json();
  res.write("456");
  res.end(); // finalise the HTTP response

  console.log(data); // log data for some reason ¯\_(ツ)_/¯
};

This will return a promise that resolves with undefined or rejects with either a networking error, HTTP error or JSON parsing error.

When calling it, you should handle any rejections accordingly

app.get("/some/route", async (res, res, next) => {
  try {
    await sendText(res);
  } catch (err) {
    console.error(err, err.text);
    next(err); // or perhaps `res.status(500).send(err)`
  }
});

I never use await and .then together before

Nor should you. It only leads to confusing code.

Phil
  • 157,677
  • 23
  • 242
  • 245