Re: [whatwg/fetch] Add a `timeout` option, to prevent hanging (#951)

@Pauan the proposal absolutely works from a technical perspective. And I know it does—if you read my opening issue carefully—because **it's the technique that Go already uses for the `Timeout` parameter to it's HTTP client**.

This proposal gives people a very clear contract:

> If you pass in the `timeout` option when using `fetch`, it will be used to guarantee that all of the data you read from the response will be read within that timeout's duration—whether you read just the headers, or the headers and the body.

That's actually exactly the contract people want in all the most common cases of using `fetch`. (As evidenced by Go using it for their default timeout logic too.)

It's beautiful because no matter what you do with the response, you're guaranteed that the request will never hang indefinitely while you're working with it. Eliminating that inherent uncertainty from TCP is the goal when using a timeout in the first place.

---

> @Pauan With your proposal, if the user does not access the body, then the timeout is cancelled. If the user does access the body, then the timeout is extended to cover the body. … So `fetch` would have to wait for an indefinite amount of time, just in case you later call `res.json()`. That's the problem. It needs to be deterministic.

That's not true. The timeout is not cancelled, it continues to tick until it finishes, or the user has fully read the body. The way the timeout works is actually pretty simple:

- `fetch` is called, a timeout is started.
  - Timeout finishes first: `fetch` is rejected with a timeout error.
  - Timeout doesn't finish first:
    - `fetch` is resolved.
    - If the user never reads the body, that's it! They're done. If they do, then…
    - `json` is called:
      - Timeout already finished: `json` is rejected with a timeout error.
      - Timeout finishes first: `json` is rejected with a timeout error.
      - Timeout doesn't finish first:
        - `json` is resolved.

If you look at the different cases that people are using `fetch` for, you'll see that this system actually works nicely:

- 90% case — using `fetch` and reading the entire body of the response, the `timeout` option covers everything that's being read, in this case the body.

- 9% case — using `fetch` and reading just the headers of the response, the `timeout` option covers everything that's being read, in this case just the headers.

- <1% case — using `fetch` and sometimes reading the headers only, but sometimes reading both the headers and the body, the `timeout` option **still** covers you for all of the data you're reading, in this case either just the headers or the headers and the body.

Covering everything that's being read—and explicitly not choosing just the headers, or just the body—is actually the goal of the `timeout`. And it's why this design is actually good.

You're describing an extreme edge case in the 1% of cases above. It's very easy to document (as Go has done) that `timeout` applies to reading all of the data from the response. And in that case, people should not make potentially endless `await` calls **between** reading the headers and reading the body **if they have opted in to using `timeout`**. That's a completely reasonable restriction to make, on an extreme edge case, to solve for the 99% cases well.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/whatwg/fetch/issues/951#issuecomment-541420513

Received on Sunday, 13 October 2019 13:55:42 UTC